bar chart transition, strange behavior d3.js - javascript

I am curious if someone can point out what I am doing wrong here? I was hoping my code would generate a smooth transition on click events, instead, first click only shifts the x-axis labels (why? how do i fix that?), then second/third do the sorting. Is there a way to prevent that from happening? I want to sort on first click and sure it would be nice if my labels didn't jump around. What am I missing here?
http://jsbin.com/cohaziqugo/edit?js,output
var data = [{"name":"A","value":0.08167},{"name":"B","value":0.01492},{"name":"C","value":0.0278},{"name":"D","value":0.04253},{"name":"E","value":0.12702},{"name":"F","value":0.02288},{"name":"G","value":0.02022},{"name":"H","value":0.06094},{"name":"I","value":0.06973},{"name":"J","value":0.00153},{"name":"K","value":0.00747},{"name":"L","value":0.04025},{"name":"M","value":0.02517},{"name":"N","value":0.06749},{"name":"O","value":0.07507},{"name":"P","value":0.01929},{"name":"Q","value":0.00098},{"name":"R","value":0.05987},{"name":"S","value":0.06333},{"name":"T","value":0.09056},{"name":"U","value":0.02758},{"name":"V","value":0.01037},{"name":"W","value":0.02465},{"name":"X","value":0.0015},{"name":"Y","value":0.01971},{"name":"Z","value":0.00074}];
var tickValues = data.map(function (d){return d.name;});
var step = Math.floor(tickValues.length / 24);
var indexes = d3.range(0,tickValues.length, step);
if (indexes.indexOf(tickValues.length - 1) == -1){
indexes.push(tickValues.length - 1);
}
var tickArray = d3.permute(tickValues, indexes);
var margin = { top: 40, right: 20, bottom: 30, left: 40 },
width = 1500 - margin.left - margin.right,
height = 600 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.domain(data.map(function (d) { return d.name; }))
.rangeBands([0, width], 0.1, 0.35);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickValues(tickArray);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var barChart = d3.select("#barbarchart1").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 + ")");
y.domain([0, 0.2]);
barChart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-0.8em")
.attr("dy", "0.15em")
.attr("transform", function(d){
return "rotate(-65)"
});
barChart.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("Test");
barChart.selectAll("#bar")
.data(data)
.enter().append("rect")
.attr("id", "bar")
.attr("x", function (d) { return x(d.name); })
.attr("width", x.rangeBand())
.attr("y", function (d) { return y(d.value); })
.attr("fill", "grey")
.attr("height", function (d) { return height - y(d.value); })
.on("click", function() {sortBars();})
.on("mouseover", function(d){
var xPos = parseFloat(d3.select(this).attr("x"));
var yPos = parseFloat(d3.select(this).attr("y"));
var height = parseFloat(d3.select(this).attr("height"));
var width = parseFloat(d3.select(this).attr("width"));
d3.select(this).attr("fill", "red");
barChart.append("text")
.attr("x",xPos)
.attr("y", yPos - 3)
.attr("font-family", "sans-serif")
.attr("font-size", "10px")
.attr("font-weight", "bold")
.attr("fill", "black")
.attr("text-anchor", "middle")
.attr("id", "tooltip")
.attr("transform", "translate(" + width/2 + ")")
.text(d.name +": "+ d.value);
})
.on("mouseout", function(){
barChart.selectAll("#tooltip").remove();
d3.select(this).attr("fill", "grey");
});
var sortOrder = true;
var sortBars = function() {
//Flip value of sortOrder
sortOrder = !sortOrder;
var x0 = x.domain(data.sort(sortOrder
? function(a, b) { return b.value - a.value; }
: function(a, b) { return d3.ascending(a.name, b.name); })
.map(function(d) { return d.name; }))
.copy();
barChart.selectAll("#bar")
.sort(function(a, b) { return x0(a.name) - x0(b.name); });
var transition = barChart.transition().duration(750),
delay = function(d, i) { return i * 50; };
transition.selectAll("#bar")
.delay(delay)
.attr("x", function(d) { return x0(d.name); });
transition.select(".x.axis")
.call(xAxis)
.selectAll("text")
.selectAll("g")
.delay(delay)
;};
function type(d) {
d.value = +d.value;
return d;
}

My solution: http://jsbin.com/cequrabuqi/edit?js,output
first click only shifts the x-axis labels
data's default sort is ascending. On first click, you flip sortOrder from true to false, causing your ternary condition to choose
function(a, b) { return d3.ascending(a.name, b.name); }) as the sort order, which is still ascending. While the code is correct, it looks broken because the sort order never changes on the first click, thus nothing happens to the bars on screen.
would be nice if my labels didn't jump around.
I don't fully understand D3, but what seems to happen is when you call transition.select(".x.axis").call(xAxis), it removes any styles you have applied. So in my solution, i reapplied the styles each time the sort function is called.

Related

D3 Scale domain does not update with selected data. I get negative values

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

how to align y axis label in D3

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.

D3 - ticks wrong positioned

I tried now couple of things but I can not figure out why my ticks are wrong positioned. I used different sources to make this stacked barchart.
Here is the fiddle of my code: http://jsfiddle.net/azj7guec/
And here is the code itself:
var margin = {top: 20, right: 20, bottom: 70, left: 40},
width = 1000 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
x = d3.scale.ordinal().rangeRoundBands([0, width], .50);
y = d3.scale.linear().range([height, 0]);
z = d3.scale.ordinal().range(["darkblue", "blue", "lightblue"])
console.log("RAW MATRIX---------------------------");
// 4 columns: ID,c1,c2,c3
var matrix = [
[22,45,34,65],
[23,66,12,22],
[24,32,44,76],
[25,12,76,32],
[26, 67, 34, 56]
];
console.log(matrix)
var keys = matrix.map(function(item){return item[0]});
console.log("REMAP---------------------------");
var remapped =["c1","c2","c3"].map(function(dat,i){
return matrix.map(function(d,ii){
return {x: d[0], y: d[i+1] };
})
});
console.log(remapped)
console.log("LAYOUT---------------------------");
var stacked = d3.layout.stack()(remapped)
console.log(stacked)
//var yMax= d3.max(stacked)
x.domain(keys);
y.domain([0, d3.max(stacked[stacked.length - 1], function(d) { return d.y0 + d.y; })]);
// show the domains of the scales
console.log("x.domain(): " + x.domain())
console.log("y.domain(): " + y.domain())
console.log("------------------------------------------------------------------");
var yAxis = d3.svg.axis()
.scale(y)
.ticks(10)
.orient("left");
var xAxis = d3.svg.axis()
.scale(x)
.tickValues(keys)
.orient("bottom");
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)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", function(d) {
return "rotate(-65)"
});
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("Open Issues");
// Add a group for each column.
var valgroup = svg.selectAll("g.valgroup")
.data(stacked)
.enter().append("g")
.attr("class", "valgroup")
.style("fill", function(d, i) { return z(i); })
.style("stroke", function(d, i) { return d3.rgb(z(i)).darker(); });
// Add a rect for each date.
var rect = valgroup.selectAll("rect")
.data(function(d){return d;})
.enter().append("rect")
.attr("width", 20)
.attr("x", function(d) { return x(d.x); })
.attr("y", function(d) { return y(d.y0 + d.y); })
.attr("height", function(d) { return y(d.y0) - y(d.y0 + d.y); });
//.attr("width", x.rangeBand());
Hope someone can help me.
The ordinal scale divides its output range into intervals based on the input domain. Your current positioning puts the bars at the beginning of those intervals, whereas the ticks are in the center. To match up the positions, add half the interval minus half the bar width to the beginning of the interval:
.attr("x", function(d) { return x(d.x) + (x.rangeBand() - 20) / 2; })
Complete demo here.

Controlling bar position with tickValues in d3.js

I'm trying to build a multi series bar chart using d3 but running into problems due to the sparse nature of the dataset.
I want to force the x-axis to have a tick for every day, even if there is no data. The test data I have can have data points that are weeks apart so I'm expecting wide areas with no bars - which is fine.
I thought I could force the xAxis to use a set of predefined ticks using the tickValues array, but these leads to very strange display of overlaying the text for each day on top of days that do have some data.
I've included a screenshot of what I mean.
I get the feeling I'm supposed to do something when calculating the width of the bars but can't figure out what that might be.
Code:
var data = [];
var tickValues = [];
var max = _.max(chartData.tabular, function(assessment) { return assessment.dateUTC; });
var min = _.min(chartData.tabular, function(assessment) { return assessment.dateUTC; });
var iter = moment.twix(min.dateUTC, max.dateUTC).iterate("days");
while(iter.hasNext()){
var momentObj = iter.next();
var assessment = _.find(chartData.tabular, {'date': momentObj.format('DD/MM/YYYY')});
tickValues.push(momentObj.valueOf());
if(assessment != null){
if(assessment.type == 'calculated'){
data.push({date: momentObj.valueOf(), calculated: assessment.score, manual: null});
}
if(assessment.type == 'manual'){
data.push({date: momentObj.valueOf(), calculated: null, manual: assessment.score});
}
}
}
log(data);
var margin = {top: 20, right: 55, bottom: 30, left: 40},
width = $('#cahai-chart').width() - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear()
.rangeRound([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickValues(tickValues)
.tickFormat(function(d){return d3.time.format('%d/%m/%y')(new Date(d))});
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var color = d3.scale.ordinal()
.range(["#001c9c","#101b4d","#475003","#9c8305","#d3c47c"]);
var svg = d3.select("#cahai-chart 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 labelVar = 'date';
var varNames = d3.keys(data[0]).filter(function (key) { return key !== labelVar;});
color.domain(varNames);
data.forEach(function (d) {
var y0 = 0;
d.mapping = varNames.map(function (name) {
return {
name: name,
label: d[labelVar],
y0: y0,
y1: y0 += +d[name]
};
});
d.total = d.mapping[d.mapping.length - 1].y1;
});
x.domain(data.map(function (d) { return d.date; }));
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("Score");
var selection = svg.selectAll(".series")
.data(data)
.enter().append("g")
.attr("class", "series")
.attr("transform", function (d) { return "translate(" + x(d.date) + ",0)"; });
selection.selectAll("rect")
.data(function (d) { return d.mapping; })
.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); })
.style("stroke", "grey")
.on("mouseover", function (d) { showPopover.call(this, d); })
.on("mouseout", function (d) { removePopovers(); })
var legend = svg.selectAll(".legend")
.data(varNames.slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function (d, i) { return "translate(55," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 10)
.attr("width", 10)
.attr("height", 10)
.style("fill", color)
.style("stroke", "grey");
legend.append("text")
.attr("x", width - 12)
.attr("y", 6)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function (d) { return d; });
function removePopovers () {
$('.popover').each(function() {
$(this).remove();
});
}
function showPopover (d) {
$(this).popover({
title: d.name,
placement: 'auto top',
container: 'body',
trigger: 'manual',
html : true,
content: function() {
return "Date: " + d3.time.format('%d/%m/%y')(new Date(d.label)) +
"<br/>Score: " + d3.format(",")(d.value ? d.value: d.y1 - d.y0); }
});
$(this).popover('show')
}
An ordinal scale will always show as many ticks are there are values in the domain. You just need to pass the full array of dates as the domain.
Replace this line
x.domain(data.map(function (d) { return d.date; }));
with this
x.domain(tickValues);
It looks like you have everything else set up correctly, so this will space the bars out along the axis and make them slimmer.

d3js - Sortable Group Bar Chart

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

Categories