I have a JSON query and I am presenting the data using a D3 chart. It seems that the grouping (Countries) does not work. We can some more than one line for each country (e.g. Canada). Part of the code:
data.forEach(function (a) {
groups[a.value] = groups[a.value] || [];
groups[a.value].push(a);
});
result = Object.keys(groups).reduce(function (r, k) {
return r.concat(groups[k]);
}, []);
Ideally, I want to make this work by changing the script. A second solution is to change the format of the JSON (manipulation). I am trying to focus on the first solution... The snippet is here:
var json_data= {"headers":["Dimension 1","Metric 1","Metric 2"],"rows":[["Australia",174,23],["Canada",502,17],["France",242,37],["Germany",102,42],["United Kingdom",126,44],["United States",1246,47],["Australia",680,80],["Canada",1241,66],["Canada",1241,66],["France",150,30],["Germany",244,22],["United Kingdom",501,9],["United States",4960,41],["Australia",9,8],["Canada",3655,70],["France",1654,95],["Germany",1190,36],["United Kingdom",1222,38],["United States",7941,53],["Australia",6829,56],["Canada",1664,75],["France",2995,88],["Germany",1487,100],["United Kingdom",9245,29],["United States",9008,66],["Australia",9376,7],["Canada",1531,31],["France",5421,22],["Germany",6975,41],["United Kingdom",4320,100],["United States",3200,41],["Australia",6688,41],["Canada",699,42],["France",5403,70],["Germany",6377,49],["United Kingdom",2471,14],["United States",6650,4],["Australia",865,70],["Canada",511,20],["France",981,36],["Germany",57,10],["United Kingdom",675,38],["United States",40,72],["Australia",400,63],["Canada",971,90],["France",357,93],["Germany",820,40],["United Kingdom",520,32],["United States",448,24],["Australia",513,40],["Canada",977,8],["France",118,84],["Germany",161,29],["United Kingdom",239,89],["United States",327,79]]};
var headers = json_data.headers;
var platform_data = json_data.rows;
var data = [];
var metric = 0;
for (var i in platform_data)
{
var dimension = platform_data[i][0];
metric = (platform_data[i][1]).toFixed(0);
object = { label: dimension, value: metric};
data.push(object);
}
//Sorting
var data = data,
groups = Object.create(null),
result = [];
data.forEach(function (a) {
groups[a.value] = groups[a.value] || [];
groups[a.value].push(a);
});
result = Object.keys(groups).reduce(function (r, k) {
return r.concat(groups[k]);
}, []);
//Descending - Reverse JSON order
var objAssetSelection = result.reverse();
data = objAssetSelection;
//D3 Code
var div = d3.select("body").append("div").attr("class", "toolTip");
var axisMargin = 20,
margin = 40,
valueMargin = 4,
width = parseInt(d3.select('body').style('width'), 10),
height = parseInt(d3.select('body').style('height'), 10),
barHeight = (height-axisMargin-margin*2)* 0.4/data.length,
barPadding = (height-axisMargin-margin*2)*0.6/data.length,
data, bar, svg, scale, xAxis, labelWidth = 0;
max = d3.max(data, function(d) { return d.value; });
svg = d3.select('body')
.append("svg")
.attr("width", width)
.attr("height", height);
bar = svg.selectAll("g")
.data(data)
.enter()
.append("g");
bar.attr("class", "bar")
.attr("cx",0)
.attr("transform", function(d, i) {
return "translate(" + margin + "," + (i * (barHeight + barPadding) + barPadding) + ")";
});
bar.append("text")
.attr("class", "label")
.attr("y", barHeight / 2)
.attr("dy", ".35em") //vertical align middle
.text(function(d){
return d.label;
}).each(function() {
labelWidth = Math.ceil(Math.max(labelWidth, this.getBBox().width));
});
scale = d3.scale.linear()
.domain([0, max])
.range([0, width - margin*2 - labelWidth]);
xAxis = d3.svg.axis()
.scale(scale)
.tickSize(-height + 2*margin + axisMargin)
.orient("bottom");
bar.append("rect")
.attr("transform", "translate("+labelWidth+", 0)")
.attr("height", barHeight)
.attr("width", function(d){
return scale(d.value);
});
bar.append("text")
.attr("class", "value")
.attr("y", barHeight / 2)
.attr("dx", -valueMargin + labelWidth) //margin right
.attr("dy", ".35em") //vertical align middle
.attr("text-anchor", "end")
.text(function(d){
return (d.value);
})
.attr("x", function(d){
var width = this.getBBox().width;
return Math.max(width + valueMargin, scale(d.value));
});
bar
.on("mousemove", function(d){
div.style("left", d3.event.pageX+10+"px");
div.style("top", d3.event.pageY-25+"px");
div.style("display", "inline-block");
div.html((d.label)+"<br>"+(d.value));
});
bar
.on("mouseout", function(d){
div.style("display", "none");
});
svg.insert("g",":first-child")
.attr("class", "axisHorizontal")
.attr("transform", "translate(" + (margin + labelWidth) + ","+ (height - axisMargin - margin)+")")
.call(xAxis);
#import url('https://fonts.googleapis.com/css?family=Roboto');
body {
font-family: "Roboto"!important;
width: 100%;
height: 500px;
position: relative;
}
svg {
width: 100%;
height: 100%;
position: center;
}
.toolTip {
position: absolute;
display: none;
width: auto;
height: auto;
background: none repeat scroll 0 0 white;
border: 0 none;
border-radius: 8px 8px 8px 8px;
box-shadow: -3px 3px 15px #888888;
color: black;
font: 12px sans-serif;
padding: 5px;
text-align: center;
}
text {
font: 10px sans-serif;
color: white;
}
text.value {
font-size: 100%;
fill: white;
}
.axisHorizontal path{
fill: none;
}
.axisHorizontal .tick line {
stroke-width: 1;
stroke: rgba(0, 0, 0, 0.2);
}
.bar {
fill: steelblue;
fill-opacity: .9;
}
<script src="https://d3js.org/d3.v3.min.js"></script>
The grouping code is incorrect. If you console log data after your grouping data, it's the same non-grouped data.
For better grouping, you can take a look at d3.nest. Here's the grouping code using d3.nest (grouping data based on label and summing up the values using d3.sum (d3.sum):
var nested_data = d3.nest()
.key(function(d) { return d.label; })
.rollup(function (d) {
return d3.sum(d, function(v) { return +v.value;})
}).entries(data);
And to not disturb your rest of the code, I'm mapping this nested_data to the original format by this:
data = nested_data.map(function(row) {
return {label: row.key, value: row.values};
});
EDIT: Adding sorting code
data.sort(function(a, b) { return a.value > b.value ? -1 : a.value === b.value ? 0 : 1; });
Using the above nesting/grouping, here's a code snippet:
var json_data= {"headers":["Dimension 1","Metric 1","Metric 2"],"rows":[["Australia",174,23],["Canada",502,17],["France",242,37],["Germany",102,42],["United Kingdom",126,44],["United States",1246,47],["Australia",680,80],["Canada",1241,66],["Canada",1241,66],["France",150,30],["Germany",244,22],["United Kingdom",501,9],["United States",4960,41],["Australia",9,8],["Canada",3655,70],["France",1654,95],["Germany",1190,36],["United Kingdom",1222,38],["United States",7941,53],["Australia",6829,56],["Canada",1664,75],["France",2995,88],["Germany",1487,100],["United Kingdom",9245,29],["United States",9008,66],["Australia",9376,7],["Canada",1531,31],["France",5421,22],["Germany",6975,41],["United Kingdom",4320,100],["United States",3200,41],["Australia",6688,41],["Canada",699,42],["France",5403,70],["Germany",6377,49],["United Kingdom",2471,14],["United States",6650,4],["Australia",865,70],["Canada",511,20],["France",981,36],["Germany",57,10],["United Kingdom",675,38],["United States",40,72],["Australia",400,63],["Canada",971,90],["France",357,93],["Germany",820,40],["United Kingdom",520,32],["United States",448,24],["Australia",513,40],["Canada",977,8],["France",118,84],["Germany",161,29],["United Kingdom",239,89],["United States",327,79]]};
var headers = json_data.headers;
var platform_data = json_data.rows;
var data = [];
var metric = 0;
for (var i in platform_data)
{
var dimension = platform_data[i][0];
metric = (platform_data[i][1]).toFixed(0);
object = { label: dimension, value: metric};
data.push(object);
}
//Sorting
var data = data,
groups = Object.create(null),
result = [];
/* data.forEach(function (a) {
groups[a.value] = groups[a.value] || [];
groups[a.value].push(a);
});
result = Object.keys(groups).reduce(function (r, k) {
return r.concat(groups[k]);
}, []); */
var nested_data = d3.nest()
.key(function(d) { return d.label; })
.rollup(function (d) {
return d3.sum(d, function(v) { return +v.value;})
}).entries(data);
data = nested_data.map(function(row) {
return {label: row.key, value: row.values};
}).sort(function(a, b) { return a.value > b.value ? -1 : a.value === b.value ? 0 : 1; });
//Descending - Reverse JSON order
/* var objAssetSelection = result.reverse();
data = objAssetSelection;
*/
//D3 Code
var div = d3.select("body").append("div").attr("class", "toolTip");
var axisMargin = 20,
margin = 40,
valueMargin = 4,
width = parseInt(d3.select('body').style('width'), 10),
height = parseInt(d3.select('body').style('height'), 10),
barHeight = (height-axisMargin-margin*2)* 0.4/data.length,
barPadding = (height-axisMargin-margin*2)*0.6/data.length,
data, bar, svg, scale, xAxis, labelWidth = 0;
max = d3.max(data, function(d) { return d.value; });
svg = d3.select('body')
.append("svg")
.attr("width", width)
.attr("height", height);
bar = svg.selectAll("g")
.data(data)
.enter()
.append("g");
bar.attr("class", "bar")
.attr("cx",0)
.attr("transform", function(d, i) {
return "translate(" + margin + "," + (i * (barHeight + barPadding) + barPadding) + ")";
});
bar.append("text")
.attr("class", "label")
.attr("y", barHeight / 2)
.attr("dy", ".35em") //vertical align middle
.text(function(d){
return d.label;
}).each(function() {
labelWidth = Math.ceil(Math.max(labelWidth, this.getBBox().width));
});
scale = d3.scale.linear()
.domain([0, max])
.range([0, width - margin*2 - labelWidth]);
xAxis = d3.svg.axis()
.scale(scale)
.tickSize(-height + 2*margin + axisMargin)
.orient("bottom");
bar.append("rect")
.attr("transform", "translate("+labelWidth+", 0)")
.attr("height", barHeight)
.attr("width", function(d){
return scale(d.value);
});
bar.append("text")
.attr("class", "value")
.attr("y", barHeight / 2)
.attr("dx", -valueMargin + labelWidth) //margin right
.attr("dy", ".35em") //vertical align middle
.attr("text-anchor", "end")
.text(function(d){
return (d.value);
})
.attr("x", function(d){
var width = this.getBBox().width;
return Math.max(width + valueMargin, scale(d.value));
});
bar
.on("mousemove", function(d){
div.style("left", d3.event.pageX+10+"px");
div.style("top", d3.event.pageY-25+"px");
div.style("display", "inline-block");
div.html((d.label)+"<br>"+(d.value));
});
bar
.on("mouseout", function(d){
div.style("display", "none");
});
svg.insert("g",":first-child")
.attr("class", "axisHorizontal")
.attr("transform", "translate(" + (margin + labelWidth) + ","+ (height - axisMargin - margin)+")")
.call(xAxis);
body {
font-family: "Roboto"!important;
width: 100%;
height: 500px;
position: relative;
}
svg {
width: 100%;
height: 100%;
position: center;
}
.toolTip {
position: absolute;
display: none;
width: auto;
height: auto;
background: none repeat scroll 0 0 white;
border: 0 none;
border-radius: 8px 8px 8px 8px;
box-shadow: -3px 3px 15px #888888;
color: black;
font: 12px sans-serif;
padding: 5px;
text-align: center;
}
text {
font: 10px sans-serif;
color: white;
}
text.value {
font-size: 100%;
fill: white;
}
.axisHorizontal path{
fill: none;
}
.axisHorizontal .tick line {
stroke-width: 1;
stroke: rgba(0, 0, 0, 0.2);
}
.bar {
fill: steelblue;
fill-opacity: .9;
}
<script src="https://d3js.org/d3.v3.min.js"></script>
To learn more about d3 nest, here's a good examples list: http://bl.ocks.org/phoebebright/raw/3176159/
Hope this helps.
I am trying to work on getting a d3.js bar chart similar to the one. I have in HTML & CSS as following :
Here is HTML Link : http://huntedhunter.com/venue_report/
Can I know if it would be possible to get this one similar like the above HTML & CSS one :
https://codepen.io/nicefellow1234/pen/aEZrma
function wrap(text, width) {
text.each(function() {
var text = d3.select(this),
words = text.text().split(/\s+/).reverse(),
word,
line = [],
lineNumber = 0,
lineHeight = 1.1, // ems
y = text.attr("y"),
dy = parseFloat(text.attr("dy")),
tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "em");
while (word = words.pop()) {
line.push(word);
tspan.text(line.join(" "));
if (tspan.node().getComputedTextLength() > width) {
line.pop();
tspan.text(line.join(" "));
line = [word];
tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
}
}
});
}
dataset = [
{label:"Art and Humanities", "Not Satisfied":25, "Not Much Satisfied":40, "Satisfied": 15, "Very Satisfied":20},
{label:"Sciences", "Not Satisfied":5, "Not Much Satisfied":30, "Satisfied": 50, "Very Satisfied":15},
{label:"Health Sciences", "Not Satisfied":20, "Not Much Satisfied":30, "Satisfied": 40, "Very Satisfied":10},
{label:"Social Sciences", "Not Satisfied":10, "Not Much Satisfied":40, "Satisfied": 45, "Very Satisfied":5},
{label:"Architecture and Engineering", "Not Satisfied":5, "Not Much Satisfied":15, "Satisfied": 40, "Very Satisfied":40},
];
var margin = {top: (parseInt(d3.select('body').style('height'), 10)/20), right: (parseInt(d3.select('body').style('width'), 10)/20), bottom: (parseInt(d3.select('body').style('height'), 10)/6), left: (parseInt(d3.select('body').style('width'), 10)/20)},
width = parseInt(d3.select('body').style('width'), 10) - margin.left - margin.right,
height = parseInt(d3.select('body').style('height'), 10) - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1,.3);
var y = d3.scale.linear()
.rangeRound([height, 0]);
var colorRange = d3.scale.category20();
var color = d3.scale.ordinal()
.range(colorRange.range());
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 + ")");
var divTooltip = d3.select("body").append("div").attr("class", "toolTip");
color.domain(d3.keys(dataset[0]).filter(function(key) { return key !== "label"; }));
dataset.forEach(function(d) {
var y0 = 0;
d.values = color.domain().map(function(name) { return {name: name, y0: y0, y1: y0 += +d[name]}; });
d.total = d.values[d.values.length - 1].y1;
});
x.domain(dataset.map(function(d) { return d.label; }));
y.domain([0, d3.max(dataset, 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", 9)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Satisfaction %");
var bar = svg.selectAll(".label")
.data(dataset)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x(d.label) + ",0)"; });
svg.selectAll(".x.axis .tick text")
.call(wrap, x.rangeBand());
var bar_enter = bar.selectAll("rect")
.data(function(d) { return d.values; })
.enter();
bar_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); });
bar_enter.append("text")
.text(function(d) { return d3.format(".2s")(d.y1-d.y0)+"%"; })
.attr("y", function(d) { return y(d.y1)+(y(d.y0) - y(d.y1))/2; })
.attr("x", x.rangeBand()/3)
.style("fill", '#ffffff');
bar
.on("mousemove", function(d){
divTooltip.style("left", d3.event.pageX+10+"px");
divTooltip.style("top", d3.event.pageY-25+"px");
divTooltip.style("display", "inline-block");
var elements = document.querySelectorAll(':hover');
l = elements.length
l = l-1
element = elements[l].__data__
value = element.y1 - element.y0
divTooltip.html((d.label)+"<br>"+element.name+"<br>"+value+"%");
});
bar
.on("mouseout", function(d){
divTooltip.style("display", "none");
});
svg.append("g")
.attr("class", "legendLinear")
.attr("transform", "translate(0,"+(height+30)+")");
var legend = d3.legend.color()
.shapeWidth(height/4)
.shapePadding(10)
.orient('horizontal')
.scale(color);
svg.select(".legendLinear")
.call(legend);
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
width: 960px;
height: 500px;
position: relative;
}
svg {
width: 100%;
height: 100%;
position: center;
}
text{
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
.toolTip {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
position: absolute;
display: none;
width: auto;
height: auto;
background: none repeat scroll 0 0 white;
border: 0 none;
border-radius: 8px 8px 8px 8px;
box-shadow: -3px 3px 15px #888888;
color: black;
font: 12px sans-serif;
padding: 5px;
text-align: center;
}
.legend {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 60%;
}
text {
font: 10px sans-serif;
}
.axis text {
font: 10px sans-serif;
}
.axis path{
fill: none;
stroke: #000;
}
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-legend/1.7.0/d3-legend.min.js"></script>
I am trying to animate the bars when the user clicks on the legend. I've built the update function - but I am unsure on how to do the required filter and animation for this feature
http://jsfiddle.net/0ht35rpb/211/
function update(){
console.log("data", data);
var barData = data.filter(function(d1) {
console.log("d1", d1);
return !d1.disabled;
});
console.log("barData", barData);
var bar = chartHolder.selectAll(".bar")
.data(data)
console.log("bar", bar);
bar.transition()
.attr("width", x0.rangeBand())
.attr("y", function(d) {
return 0;
//return y(d.value);
})
.attr("height", function(d) {
return 0;
//return height - y(d.value);
});
bar.exit().remove();
/*
var bar = bar.selectAll("rect")
bar.transition()
//.attr("id", function(d){ return 'tag'+d.state.replace(/\s|\(|\)|\'|\,+/g, '');})
.attr("x", function(d) { return x1(d.name); })
.attr("width", x0.rangeBand())
.attr("y", function(d) {
return 0;
//return y(d.value);
})
.attr("height", function(d) {
return 0;
//return height - y(d.value);
});
//bar.exit().remove();
*/
This code var bar = chartHolder.selectAll(".bar") returns empty selection. You should select all .bars apply new data with method data, after that select all react, set animation parameters transition, duration, delay and set new values for approptiate attributes.
I rewrite your code - http://jsfiddle.net/168x28p3/1/ Here, I simply animate height of bars after click on legend. But this way you can create the another animation which you need.
var $this = document.querySelector('.chart');
var data = [{
label: "a",
"Current Period": 20,
"Prior Year": 10
}, {
label: "b",
"Current Period": 15,
"Prior Year": 30
}, {
label: "c",
"Current Period": 25,
"Prior Year": 40
}, {
label: "d",
"Current Period": 5,
"Prior Year": 60
}];
var configuration = [{
"yLabel": "People Count"
}];
var w = 660;
var h = 500;
function colores_google(n) {
var colores_g = ["#f7b363", "#448875", "#c12f39", "#2b2d39", "#f8dd2f", "#8bf41b"];
return colores_g[n % colores_g.length];
}
var margin = {
top: 20,
right: 110,
bottom: 30,
left: 20
},
width = w - margin.left - margin.right,
height = h - margin.top - margin.bottom;
var svg = d3.select($this).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 axisHolder = svg.append("g")
.attr("class", "axis");
var chartHolder = svg.append("g")
.attr("class", "chart");
var legendHolder = svg.append("g")
.attr("class", "legend");
var x0 = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var x1 = d3.scale.ordinal();
var y = d3.scale.linear()
.range([height, 0]);
//var colorRange = d3.scale.category20();
//var color = d3.scale.ordinal()
//.range(colorRange.range());
var xAxis = d3.svg.axis()
.scale(x0)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".2s"));
var divTooltip = d3.select("body").append("div").attr("class", "toolTip");
var options = d3.keys(data[0]).filter(function(key) {
return key !== "label";
});
data.forEach(function(d) {
d.valores = options.map(function(name) {
return {
name: name,
value: +d[name]
};
});
});
x0.domain(data.map(function(d) {
return d.label;
}));
x1.domain(options).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function(d) {
return d3.max(d.valores, function(d) {
return d.value;
});
})]);
axisHolder.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
axisHolder.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(configuration[0].yLabel);
var bar = chartHolder.selectAll(".bar")
.data(data)
.enter().append("g")
.attr("class", "bars")
.attr("transform", function(d) {
return "translate(" + x0(d.label) + ",0)";
});
bar.selectAll("rect")
.data(function(d) {
return d.valores;
})
.enter().append("rect")
.attr("width", x1.rangeBand())
.attr("x", function(d) {
return x1(d.name);
})
.attr("y", function(d) {
return y(d.value);
})
.attr("value", function(d) {
return d.name;
})
.attr("height", function(d) {
return height - y(d.value);
})
.style("fill", function(d, i) {
return colores_google(i);
});
bar
.on("mousemove", function(d) {
divTooltip.style("left", d3.event.pageX + 10 + "px");
divTooltip.style("top", d3.event.pageY - 25 + "px");
divTooltip.style("display", "inline-block");
var x = d3.event.pageX,
y = d3.event.pageY
var elements = document.querySelectorAll(':hover');
l = elements.length
l = l - 1
elementData = elements[l].__data__
divTooltip.html((d.label) + "<br>" + elementData.name + "<br>" + elementData.value + "%");
});
bar
.on("mouseout", function(d) {
divTooltip.style("display", "none");
});
var legend = legendHolder.selectAll(".legend")
.data(options.slice())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) {
return "translate(" + margin.right + "," + i * 20 + ")";
});
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", function(d, i) {
return colores_google(i);
})
.on("click", function(d) {
console.log("d", d);
d.disabled = !d.disabled;
update();
});
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) {
return d;
});
function update() {
var barData = data.map(item => {
item.valores = item.valores.map(valor => {
return Object.assign({}, valor, { value: Math.random() * 40 })
})
return item;
})
var bar = chartHolder.selectAll(".bars")
.data(barData)
var rect = bar.selectAll("rect")
.data(function(d) {
return d.valores;
})
rect
.transition()
.duration(1000)
.delay(100)
.attr("width", x0.rangeBand() / 2)
.attr("y", function(d) {
return y(d.value);
})
.attr("height", function(d) {
return height - y(d.value);
});
}
* {
margin: 0;
padding: 0;
border: 0;
}
body {
background: #ffd;
}
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
position: relative;
}
text {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
.toolTip {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
position: absolute;
display: none;
width: auto;
height: auto;
background: none repeat scroll 0 0 white;
border: 0 none;
border-radius: 8px 8px 8px 8px;
box-shadow: -3px 3px 15px #888888;
color: black;
font: 12px sans-serif;
padding: 5px;
text-align: center;
}
.legend {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 60%;
}
rect {
stroke-width: 2;
}
text {
font: 10px sans-serif;
}
.axis text {
font: 10px sans-serif;
}
.axis path {
fill: none;
stroke: #000;
}
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.axis .tick line {
stroke-width: 1;
stroke: rgba(0, 0, 0, 0.2);
}
.axisHorizontal path {
fill: none;
}
.axisHorizontal line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.axisHorizontal .tick line {
stroke-width: 1;
stroke: rgba(0, 0, 0, 0.2);
}
.bar {
fill: steelblue;
fill-opacity: .9;
}
/*
.x.axis path {
display: none;
}*/
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div class="chart"></div>
I want to create stacked bar chart with d3.
I have this data in CSV file:
Type Sum Color
Regular 29756.85897 green
Regular 9756.85897 blue
and I want that each row will appear above the other in Y axis.
for example in this photo, the blue area should start in y=9756 until y=39512.
what should I change?
this is the relevant html code:
the whole code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
margin:auto;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.bar1 {
fill: #00FF66;
}
.bar1:hover {
fill: black ;
}
.x.axis path {
display: none;
}
.d3-tip {
line-height: 1;
font-weight: bold;
padding: 12px;
background: rgba(0, 0, 0, 0.8);
color: #fff;
border-radius: 2px;
}
/* Creates a small triangle extender for the tooltip */
.d3-tip:after {
box-sizing: border-box;
display: inline;
font-size: 10px;
width: 100%;
line-height: 1;
color: rgba(0, 0, 0, 0.8);
content: "\25BC";
position: absolute;
text-align: center;
}
/* Style northward tooltips differently */
.d3-tip.n:after {
margin: -1px 0 0 0;
top: 100%;
left: 0;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://labratrevenge.com/d3-tip/javascripts/d3.tip.v0.6.3.js"></script>
<script>
var margin = {top: 80, right: 90, bottom: 30, left: 90},
width = 1000 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var formatPercent = d3.format(".0%");
//יצירת X
//יאכלס את סוגי הרכב השונים
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
//יצירת ציר y
//יציג בר עבור מחיר הרכב המוצע לדילרים
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
//יצירת ציר הY
//והצמדתו לצד שמאל
var yAxis = d3.svg.axis()
.scale(y)
.orient("left").ticks(4)
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function(d) {
return "<strong></strong>"+d.Type+"<br><strong></strong> <span style='color:#00FF66'>" + d.Sum + "</span>";
})
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.call(tip);
//קליטת הטבלה והגדרת הטווחים על הצירים
d3.csv("Targil2.csv", type, function(error, data) {
x.domain(data.map(function(d) { return d.Type; }));
y.domain([0, d3.max(data, function(d) { return d.Sum*2; })]);
var stack = d3.layout.stack();
.x(function(d) { return d.Type }) // tell d3 to use Type as x value
.y(function(d) { return d.Sum }); // tell d3 to use Sum as y value
var stackData = stack(data);
//הוספה של 2 הצירים
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis axisLeft")
.attr("transform", "translate(0,0)")
.call(yAxis)
.append("text")
.attr("y", 6)
.attr("dy", "-2em")
.style("text-anchor", "end")
.style("text-anchor", "end")
.text("Price");
//הוספת בר הנתונים
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("x", function(d) { return x(d.Type); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return d.y0 })
.attr("height", function(d) { return (height - y(d.Sum)); })
.style("fill", function(d){
if(d["Color"] == "green"){ return "green";}
else return "#0066FF";})
.on('mouseover', tip.show)
.on('mouseout', tip.hide)
});
function type(d) {
d.Sum = +d.Sum;
return d;
}
</script>
</body>
</html>
I tried to use that stack function as you told me, and changed the attribute of "y" , but it's not work for me now. I think I did something wrong.
Here you go.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
margin:auto;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.bar1 {
fill: #00FF66;
}
.bar1:hover {
fill: black ;
}
.x.axis path {
display: none;
}
.d3-tip {
line-height: 1;
font-weight: bold;
padding: 12px;
background: rgba(0, 0, 0, 0.8);
color: #fff;
border-radius: 2px;
}
/* Creates a small triangle extender for the tooltip */
.d3-tip:after {
box-sizing: border-box;
display: inline;
font-size: 10px;
width: 100%;
line-height: 1;
color: rgba(0, 0, 0, 0.8);
content: "\25BC";
position: absolute;
text-align: center;
}
/* Style northward tooltips differently */
.d3-tip.n:after {
margin: -1px 0 0 0;
top: 100%;
left: 0;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://labratrevenge.com/d3-tip/javascripts/d3.tip.v0.6.3.js"></script>
<script>
var margin = {top: 80, right: 90, bottom: 30, left: 90},
width = 1000 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var formatPercent = d3.format(".0%");
//יצירת X
//יאכלס את סוגי הרכב השונים
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
//יצירת ציר y
//יציג בר עבור מחיר הרכב המוצע לדילרים
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
//יצירת ציר הY
//והצמדתו לצד שמאל
var yAxis = d3.svg.axis()
.scale(y)
.orient("left").ticks(4)
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function(d) {
return "<strong></strong>"+d.Type+"<br><strong></strong> <span style='color:#00FF66'>" + d.Sum + "</span>";
})
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.call(tip);
//קליטת הטבלה והגדרת הטווחים על הצירים
d3.csv("Targil2.csv", type, function(error, data) {
window.dataSet = data;
data.sort(function(x,y){
var a = x.Sum;
var b = y.Sum;
return a > b ? -1 : a < b ? 1 : 0
})
x.domain(data.map(function(d) { return d.Type; }));
y.domain([0, d3.max(data, function(d) { return d.Sum*2; })]);
var stack = d3.layout.stack()
.x(function(d) { return d.Type }) // tell d3 to use Type as x value
.y(function(d) { return d.Sum }); // tell d3 to use Sum as y value
// var stackData = stack(data);
//הוספה של 2 הצירים
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis axisLeft")
.attr("transform", "translate(0,0)")
.call(yAxis)
.append("text")
.attr("y", 6)
.attr("dy", "-2em")
.style("text-anchor", "end")
.style("text-anchor", "end")
.text("Price");
var stackSoFar = 0;
//הוספת בר הנתונים
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("x", function(d) { return x(d.Type); })
.attr("width", x.rangeBand())
.attr("y", function(d){
d3.select(this)
.attr("height", function(d2){
var thisHeight = height - y(d.Sum);
stackSoFar += thisHeight
return thisHeight
});
return (height - stackSoFar)
})
.style("fill", function(d){
if(d["Color"] == "green"){ return "green";}
else return "#0066FF";})
.on('mouseover', tip.show)
.on('mouseout', tip.hide)
});
function type(d) {
d.Sum = +d.Sum;
return d;
}
</script>
</body>
</html>
first of all, I guess that when you say
for example in this photo, the blue area should start in y=9756 until
y=39512
You actually mean
for example in this photo, the blue area should start in y=29756 until
y=39512
What happens is that your green area is painted from 0 to 29756 then your blue area on top of it from 0 to 9756. You need to shift each area on top of the previous one.
Easiest is to preprocess your data to do it.
D3.js can do it for you, see Stack Layout. This computes the y0 and y for all of your layers.
EDIT:
var stack = d3.layout.stack()
.x(function(d) { return d.Type }) // tell d3 to use Type as x value
.y(function(d) { return d.Sum }); // tell d3 to use Sum as y value
var stackData = stack(data);
After that, you data is augmented, i.e. each entry contains an additional y and y0 values that you can use directly to plot (in your y and height attribute).
I want to have "tooltips" on my line chart permanently (in, other words, I want labels). Code I have written for this is below, but it doesn't even show any such tooltip/label on my chart. What would be the problem?
<!DOCTYPE html>
<style>
body {
font: 15px sans-serif;
}
.axis line, .axis path {
fill: none;
stroke: gray;
stroke-width:4;
shape-rendering: crispEdges;
}
div.tooltip {
position: absolute;
text-align: center;
width: 60px;
height: 28px;
padding: 2px;
font: 12px sans-serif;
background: lightsteelblue;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
.line {
fill: none;
stroke: steelblue;
stroke-width: 3px;
}
</style>
<html>
<body>
<script type="text/javascript" src="d3.v3.min.js"></script>
<script type="text/javascript">
var data = [
{time:0,ftRem1:100,ftRem2:100,ftRem3:100},{time:4,ftRem1:95.7,ftRem2:89.4,ftRem3:88.9},
{time:4.5,ftRem1:94.9,ftRem2:87.3,ftRem3:86.7},{time:8.5,ftRem1:93.8,ftRem2:84.3,ftRem3:83.2},
{time:32.5,ftRem1:91.2,ftRem2:76.8,ftRem3:73.7},{time:56.5,ftRem1:87.8,ftRem2:67.1,ftRem3:61.7},
{time:58.5,ftRem1:87.5,ftRem2:66.4,ftRem3:60.8},{time:82.5,ftRem1:83.7,ftRem2:55.7,ftRem3:47.6},
{time:94.5,ftRem1:82.3,ftRem2:51.5,ftRem3:42.4}
];
var margin = {top: 20,left: 30, bottom: 30,right: 40},
height = 500 - margin.top - margin.bottom,
width = 900 - margin.left - margin.right;
var color = d3.scale.category10();
var x = d3.scale.linear()
.range([0,width]);
var y = d3.scale.linear()
.range([height,0]);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var chart = 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 line = d3.svg.line()
.x(function(d){return x(d.hours);})
.y(function(d){return y(d.ftRem);})
.interpolate("linear");
color.domain(d3.keys(data[0]).filter(function(d){ return d!='time' }));
var points = color.domain().map(function(duration){
return {
ftPoints:duration,
values: data.map(function(d){
return{hours: d.time,ftRem: d[duration]};
})
}
});
x.domain([0,d3.max(data,function(d){return d.time})]);
y.domain([
0,
d3.max(points,function(c){ return d3.max(c.values,function(d){ return d.ftRem;});})
]);
chart.append("g")
.attr("class","x axis")
.attr("transform","translate(0,"+height+")")
.call(xAxis);
chart.append("g")
.attr("class","y axis")
.call(yAxis);
var freshTime=chart.selectAll(".ftpoint")
.data(points)
.enter().append("g")
.attr("class","ftpoint");
freshTime.append("path")
.attr("class","line")
.attr("d",function(d,i){return line(d.values);})
var lineColor = ["#1abc9c","#3498db","#e74c3c"];
freshTime.select(".line")
.style("stroke",function(d,i){return lineColor[i];})
Step = ["","a","b","c","d","e","f","g","h"];
var tooltip = chart.selectAll(".tooltip")
.data(points[0].values)
.enter()
.append("div")
.attr("class","tooltip")
.style("left", function (d) { return y(d.hours)+"px";})
.style("top", function (d) { return x(d.ftRem)+"px";})
.text(function(d,i) { return Step[i]; });
</script>
</body>
</html>
You would be better off if you use just svg elements within your chart. Its just not practical to mix and match svg and divs. So, you can just use svg element text for labels (you created class "tooltip" for the purpose of labels, maybe you should change the name of the class, since you actually want labels, not tooltips):
var tooltip = chart.selectAll(".tooltip")
.data(points[0].values)
.enter()
.append("text")
.attr("class","tooltip")
.attr("y", function (d) { return y(d.hours)+"px";})
.attr("x", function (d) { return x(d.ftRem)+"px";})
.text(function(d,i) { return Step[i]; });
(note: if you make this change, labels will appear, but I guess their positions are not Ok, but thats problem of different kind that you must solve)