I'm a d3 novice trying to create a simple, two-series bar chart that transitions when different buttons are clicked. The original chart is constructed:
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(["#d4d4d4", "#58bd5b",]);
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("div.d3space").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("/assets/data/data3.csv", function(error, data) {
var hourBuckets = d3.keys(data[0]).filter(function(key) { return key !== "Client"; });
data.forEach(function(d) {
d.hours = hourBuckets.map(function(name) { return {name: name, value: +d[name]}; });
});
x0.domain(data.map(function(d) { return d.Client; }));
x1.domain(hourBuckets).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function(d) { return d3.max(d.hours, 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("Hours");
var client = svg.selectAll(".client")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x0(d.Client) + ",0)"; });
client.selectAll("rect")
.data(function(d) { return d.hours; })
.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(hourBuckets.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; });
});
The csv being accessed is in the following format:
Client,Planned,Actual
ICC,25,50
RNR,50,47.5
MB,10,2.5
This chart renders as desired. The piece I am struggling with is getting this graph to transition to reflect different data when a link is clicked (link has id="fourweeks"). I have tried this onclick function:
window.onload = function() {
var a = document.getElementById("fourweeks");
var b = document.getElementById("eightweeks");
var c = document.getElementById("twelveweeks");
a.onclick = function() {
d3.csv("/assets/data/data1.csv", function(error, data) {
var hourBuckets = d3.keys(data[0]).filter(function(key) { return key !== "Client"; });
data.forEach(function(d) {
d.hours = hourBuckets.map(function(name) { return {name: name, value: +d[name]}; });
});
var client = svg.selectAll(".client")
client.selectAll("rect")
.data(function(d) { return d.hours; })
.transition()
.duration(1000)
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
});
}
}
...no dice. I can get this to work when creating / transitioning simple one-series bar charts that use list inputs, but not the multi-series csv ones. data2.csv is the exact same file as data1.csv, with the values adjusted slightly.
Thanks for your time reading - any advice?
First svg.selectAll(".client") returns an empty selection, because you gave these elements the class 'g' instead of 'client'.
Secondly you need to update the data of the .client-elements:
var client = svg.selectAll(".client")
.data(data);
btw. you should use selection.classed() instead of selection.attr('class')
Related
I start with d3 using version 3.
I created a grouped bar chart that looks like this:
I added a drop-down list that contains all the genders (Woman (femme), Couple...).
I'm blocked because I can't highlight the bars that correspond to a certain gender when I choose from the drop-down list.
I found on the internet that I could use this code:
d3.select('#inds')
.on("change", function () {
var sect = document.getElementById("inds");
var section = sect.options[sect.selectedIndex].value;
//some code here
});
the "Section" part contains my selected gender
I would like that when I select, for example torque, that the bars corresponding to the torque remain in color and the others are in gray.
This is the all code :
source : https://bl.ocks.org/hydrosquall/7966e9c8e8414ffcd8b5
Highlight : https://bl.ocks.org/bricedev/0d95074b6d83a77dc3ad
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 600,
height = 250 ;
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(["#54E868", "#54CCE8", "#6395FF", "#50FFC5"]);
var xAxis = d3.svg.axis()
.scale(x0)
.tickSize(0)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
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("../CSV/GenreOrigine.csv", function(error, data) {
if (error) throw error;
//Couples, Famille...
var genreNames = d3.keys(data[0]).filter(function(key) { return key !== "Origine"; });
data.forEach(function(d) {
//Valeur pour chacune des origines par rapport au genre
d.genres = genreNames.map(function(name) { return {name: name, value: +d[name]}; });
});
x0.domain(data.map(function(d) { return d.Origine; }));
x1.domain(genreNames).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function(d) { return d3.max(d.genres, 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", 0.3)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Nombre de personnes");
svg.select('.y').transition().duration(500).delay(1300).style('opacity','1');
var Origine = svg.selectAll(".Origine")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x0(d.Origine) + ",0)"; });
Origine.selectAll("rect")
.data(function(d) { return d.genres; })
.enter().append("rect")
.attr("width", x1.rangeBand())
.attr("x", function(d) { return x1(d.name); })
.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); })
.on("mouseover", function(d) {
d3.select(this).style("fill", d3.rgb(color(d.name)).darker(2));
})
.on("mouseout", function(d) {
d3.select(this).style("fill", color(d.name));
});
Origine.selectAll("rect")
.transition()
.delay(function (d) {return Math.random()*1000;})
.duration(1000)
var legend = svg.selectAll(".legend")
.data(genreNames.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; });
d3.select('#inds')
.on("change", function () {
var sect = document.getElementById("inds");
var section = sect.options[sect.selectedIndex].value;
//some code here
});
});
You can select all the rect in the SVG and filter them in the function, so the code would look like:
var sect = document.getElementById('inds');
var section = sect.options[sect.selectedIndex].value;
if(section !== '') {
d3.selectAll("rect")
.attr('opacity', function(d) {
if(d.name !== section) {
return 0.2;
} else {
return 1;
}
})
} else {
d3.selectAll('rect')
.attr('opacity', 1)
}
In the example above, I changed the opacity attribute, but you can change fill with the same approach.
I am developing bar graph using d3.js integrating with angular js.I am new to d3.js. I dont know how we can limt the the no.of x and y axis ticks.
The working is given below
mainApp.directive('ngTest', function() {
return {
restrict: 'AE',
scope: {
data: '='
},
link: function (scope, element) {
var margin = {top: 20, right: 30, bottom: 30, left: 60},
width = 410 - margin.left - margin.right,
height = 230 - margin.top - margin.bottom;
var chart = d3.select(element[0])
.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 x = d3.scale.ordinal().rangeRoundBands([0, width], .1);
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function(d) {
return "<strong>Frequency:</strong> <span style='color:red'>" + d.value + "</span>";
});
chart.call(tip);
//Render graph based on 'data'
scope.render = function(data) {
var y = d3.scale.linear()
.range([height, 0])
.domain(d3.extent(data, function(d) { return d.value; }))
.nice();
var xAxis = d3.svg.axis().scale(x).orient("bottom");
var yAxis = d3.svg.axis().scale(y).orient("left");
x.domain(data.map(function(d) { return d.name; }));
//Redraw the axes
chart.selectAll('g.axis').remove();
chart.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(-20)";
});
chart.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0-margin.left)
.attr("x",0-(height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Value");
chart.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", function(d) { return d.value < 0 ? "bar negative" : "bar positive"; })
.attr("x", function(d) { return x(d.name); })
.attr("y", height)
.attr("height", 0)
.on('mouseover', tip.show)
.on('mouseout', tip.hide)
.transition().duration(2000)
.attr("y", function(d) {return y(Math.max(0, d.value)); })
.attr("height", function(d) {return Math.abs(y(d.value) - y(0)); })
// .attr("width", x.rangeBand());
.attr("width", Math.min.apply(null, [x.rangeBand()-2, 100]));
};
scope.$watch('data', function() {
scope.render(scope.data);
}, true);
}
};
});
The working example is given in following fiddle adderss
http://jsfiddle.net/HB7LU/9000/
Use ticks method of d3 axis. Since tick format of x axis is time, you might specify both a count and a tick format.
var xAxis = d3.svg.axis().scale(x).orient("bottom").ticks(d3.time.day, 2);
var yAxis = d3.svg.axis().scale(y).orient("left").ticks(5);
You can refer more about d3 svg axis from here and about time formats from here
I've adapted this code from the multi-line line chart example here. The biggest issue I'm now having after researching what changes I needed to make is that the data lines disappear when I use .rangePoints on the x-axis ordinal scale. With just .range, the x-axis displays nothing and the data lines are bunched up along the left side of the y-axis. This has something to do with the fact I altered the original code from a time scale to ordinal, but I'm stumped as to what further changes I need to make.
Code below:
var margin = {top: 20, right: 80, bottom: 30, left: 50},
width = 500 - margin.left - margin.right,
height = 280 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangePoints([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.category10();
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var line = d3.svg.line()
.interpolate("linear")
.x(function(d) { return x(d.episodes); })
.y(function(d) { return y(d.season); });
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) {
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "episodes"; }));
var seasons = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {date: d.episodes, season: +d[name]};
})
};
});
x.domain(data.map(function(d) { return d.episodes; }));
y.domain([
d3.min(seasons, function(c) { return d3.min(c.values, function(v) { return v.season; }); }),
d3.max(seasons, function(c) { return d3.max(c.values, function(v) { return v.season; }); })
]);
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("Viewership (in mlns)");
var s = svg.selectAll(".city")
.data(seasons)
.enter().append("g")
.attr("class", "city");
s.append("path")
.attr("class", "line")
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d) { return color(d.name); });
s.append("text")
.datum(function(d) { return {name: d.name, value: d.values[d.values.length - 1]}; })
.attr("transform", function(d) { return "translate(" + x(d.value.date) + "," + y(d.value.season) + ")"; })
.attr("x", 3)
.attr("dy", ".35em")
.text(function(d) { return d.name; });
});
I`m using d3.js, Sortable Barchart with the following code:
function countrydownloads(input) {
d3.select(".barchartCountryDownloads").select("svg").remove();
var data = [{"country":"Austria","downloads":"10000"},{"country":"Germany","downloads":"20000"},{"country":"Spain","downloads":"30000"}];
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var formatPercent = d3.format(".0%");
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1, 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")
.tickFormat(formatPercent);
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 + ")");
data.forEach(function(d) {
d.downloads = +d.downloads;
});
x.domain(data.map(function(d) { return d.country; }));
y.domain([0, d3.max(data, function(d) { return d.downloads; })]);
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.country); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.downloads); })
.attr("height", function(d) { return height - y(d.downloads); });
d3.select("input").on("change", change);
var sortTimeout = setTimeout(function() {
d3.select("input").property("checked", true).each(change);
}, 2000);
function change() {
clearTimeout(sortTimeout);
// Copy-on-write since tweens are evaluated after a delay.
var x0 = x.domain(data.sort(this.checked
? function(a, b) { return b.downloads - a.downloads; }
: function(a, b) { return d3.ascending(a.country, b.country); })
.map(function(d) { return d.country; }))
.copy();
var transition = svg.transition().duration(750),
delay = function(d, i) { return i * 50; };
transition.selectAll(".bar")
.delay(delay)
.attr("x", function(d) { return x0(d.country); });
transition.select(".x.axis")
.call(xAxis)
.selectAll("g")
.delay(delay);
};
}
I don`t get anything displayed. I tried Barchart with the same kind of data, there it worked. Where is the mistake with Sortable Barchart?
.append("g:")
should be
.append("g")
If you haven't used them before, you might want to check out the chrome dev tools. Small typos like this are a lot easier to debugger when you can step through your code to see exactly what line is is causing the error to occur.
We are plotting a multilinear graph using d3.v2.js .
We are using ordinal scale for x-axis and linear scale for y-axis as we have labels(string) to be shown in x-axis
and numbers to be shown in y-axis.
In some cases graph appears to be fine , but in some cases it plots x-axis and y-axis independently and y-axis values are not in sync with x-axis values.
Also plotting starts from 0 instead of first x-axis value.
Any pointers to this issue would be of great help.
Thanks in advance.
Please find the code below.
function plotMOAGraph(data , sigPathways){
var margin = {top: 20, right: 80, bottom: 30, left: 100},
width = 960 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
var x = d3.scale.ordinal(
(d3.range(0,sigPathways.length))).rangeBands([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.category10();
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var line = d3.svg.line()
.interpolate("basis")
.x(function(d) { return x(d.pathway); })
.y(function(d) { return y(d.score); });
var svg = d3.select("#graphDiv").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", 450 + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "pathway"; }));
var entityNames = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {pathway: d.pathway, score: +d[name]};
})
};
});
x.domain(sigPathways);
y.domain([
d3.min(entityNames, function(c) { return d3.min(c.values, function(v) { return v.score; }); }),
d3.max(entityNames, function(c) { return d3.max(c.values, function(v) { return v.score; }); })
]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("dy", "9em")
.attr("dx","40em")
.style("text-anchor", "end")
.text("Pathways");
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "-4em")
.style("text-anchor", "end")
.text("Pathway Scores");
var tooltip = d3.select("#graphDiv")
.append("div")
.style("visibility", "hidden")
;
var rotateXAxis = function(d) {
var name = d.substr(0, 15);
if(name !== d) {
name = name + " ... ";
}
var el = d3.select(this);
el.text('').attr("transform", "rotate(-45)").attr("text-anchor", "end").on("mouseover", showTooltip).on("mouseout",hideTooltip);
var tspan = el.append('tspan').text(name);
tspan.attr('x', 0).attr('dy', '0');
};
svg.selectAll('g.x.axis g text').each(rotateXAxis);
function showTooltip(d) {
tooltip.text(d).style("position","absolute")
.style("top", (d3.event.pageY)-10+"px")
.style("left", (d3.event.pageX)-300+"px")
.style("visibility", "visible")
.style("font-size", "12px");
}
function hideTooltip() {
tooltip.style("visibility", "hidden");
}
var entityName = svg.selectAll(".entityName")
.data(entityNames)
.enter().append("g")
.attr("class", "entityName");
entityName.append("path")
.attr("class", "line")
.attr("d", function(d) {
return line(d.values); })
.style("stroke", function(d) {return color(d.name); });
entityName.append("text")
.datum(function(d) { return {name: d.name, value: d.values[d.values.length - 1]}; })
.attr("transform", function(d) { return "translate(" + x(d.value.pathway) + "," + y(d.value.score) + ")"; })
.attr("x", 3)
.attr("dy", ".35em")
.text(function(d) { return d.name; });
}