Related
Getting NaN on the Y axis for a d3.js bar chart.
Question relates to this one .
The answer in that question has static data, which is identical to the Ajax JSON. See commented line for const data But the Ajax data is not working.
The data is loaded but the column with data has no height as there is no Y scale data.
Console log:
Error: <rect> attribute y: Expected length, "NaN".
Error: <rect> attribute height: Expected length, "NaN".
Error: <text> attribute y: Expected length, "NaN".
Chart with Ajax loaded data:
var margin = {top: 50, right: 135, bottom: 70, left: 80},
width = 1050 - margin.left - margin.right,
height = 540 - margin.top - margin.bottom;
var svg = d3.select("#domains")
.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 + ")");
//const data = [{"Domain":"Knowledge","Knowledge":0},{"Domain":"Problem Solving","problem_solving":0},{"Domain":"Skill","skill":0},{"Domain":"Transferable","transferable":100}];
d3.json("json/domains.json", function(error, data) {
const normalized = data.map(item => {
const name = item['Domain'];
const attr = name.toLowerCase().replace(' ', '_');
const value = item[attr];
return {name, value};
});
console.log('N: ', normalized);
/*
// Transpose the data into layers
var dataset = d3.layout.stack()(["Knowledge", "Problem Solving, Skill, Transferable"].map(function(lvl) {
return data.map(function(d) {
return {
x: d.Domain,
y: d[lvl]
};
});
}));
var disciplines = d3.nest()
.key(function(d){return d.Domain})
.rollup(function(leaves){
return d3.sum(leaves, function(d) {return d3.sum(d3.values(d))});
})
.entries(data);
*/
// Set x, y and colors
var x = d3.scale.ordinal()
.domain(normalized.map(item => item.name))
.rangeRoundBands([10, width-10], 0.35, 0);
const maxValue = normalized.reduce((max, item) => Math.max(max, item.value), 0);
var y = d3.scale.linear()
.domain([0, maxValue])
.range([height, 0]);
var colors = ["#83d1c4", "#f17950", "#838BD1", "#F150BE"];
// Define and draw axes
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(5)
.tickSize(-width, 0, 0)
.tickFormat(function(d) {
return d;
});
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.outerTickSize(0)
d3.select('.y axis .tick:first-child').remove();
/*
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-0, 0])
.html(function(d) {
return d.y + '%';
})
svg.call(tip);
*/
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(0,0)")
.call(yAxis);
svg.append("g")
.call(xAxis)
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("text")
.attr("x", 390 )
.attr("y", 480 )
.style("text-anchor", "middle")
.text("Domains");
svg.append("text")
.attr("x", -200 )
.attr("y", -40 )
.attr("transform", "rotate(-90)" )
.attr('style', 'font-size:12px')
.style("text-anchor", "middle")
.text("Percentage of Learning Events");
// Create groups for each series, rects for each segment
var groups = svg.selectAll("g.group")
.data(normalized)
.enter().append("g")
.attr("class", "group")
.style("fill", function(d, i) { return colors[i]; });
groups.append("rect")
.attr("y", function(d) { return y(d.value); })
.attr("x", d => x(d.name))
.attr("height", function(d) { return y(0) - y(d.value); })
.attr('class', 'segment')
.attr("width", x.rangeBand())
// .on('mouseover', tip.show)
// .on('mouseout', tip.hide);
columns = svg.append("g")
.selectAll("text")
.data(normalized)
.enter().append("text")
.attr("x", function(d){
return x(d.name) + x.rangeBand()/2
})
.attr("y", function (d) {
return y(d.value);
})
.attr("dy", "-0.7em")
.attr('style', 'font-size:11px')
.text( function (d){
return d3.format(".2f")(d.value) + '%';
})
.style({fill: 'black', "text-anchor": "middle"});
});
Chart with static data:
Although your dataset had an typo, which can break your current setup if you had the same in the json output. We can not be that sure if there is no data provided in example from actual json response, which can be different than what's in your const data example
const data = [{"Domain":"Knowledge","Knowledge":0},{"Domain":"Problem Solving","problem_solving":0},{"Domain":"Skill","skill":0},{"Domain":"Transferable","transferable":100}];
Try with lowercase "knowledge":0 which was printing the chart correctly on my screen
https://jsfiddle.net/978ync63/
I am trying to create a grouped bar chart using D3.js. I have followed the examples provided in the D3 wiki at GitHub and have a semi working graph. However, it seems like all datapoints for a certain value get plotted at the same spot.
my data looks is a JSON array, which looks like this
[{"experiment":30385,"c":1,"ratio":0.022,"stdev":0.363,"median":0.032,"zscore":6.359},
{"experiment":30385,"c":2,"ratio":-0.02,"stdev":0.351,"median":-0.005,"zscore":-4.786},
{"experiment":30385,"c":3,"ratio":0.074,"stdev":0.339,"median":0.089,"zscore":29.036},
{"experiment":30385,"c":4,"ratio":-0.077,"stdev":0.361,"median":-0.065,"zscore":-25.704},
{"experiment":30385,"c":5,"ratio":-0.354,"stdev":0.569,"median":-0.223,"zscore":-145.625},
{"experiment":30385,"c":6,"ratio":-0.02,"stdev":0.352,"median":-0.007,"zscore":-2.545},
{"experiment":30385,"c":7,"ratio":0.018,"stdev":0.346,"median":0.036,"zscore":7.412},
{"experiment":30385,"c":8,"ratio":-0.11,"stdev":0.348,"median":-0.096,"zscore":-37.69},
{"experiment":30385,"c":9,"ratio":-0.012,"stdev":0.357,"median":0.008,"zscore":-4.394},
{"experiment":30385,"c":10,"ratio":-0.054,"stdev":0.366,"median":-0.036,"zscore":-14.158},
{"experiment":30385,"c":11,"ratio":-0.071,"stdev":0.344,"median":-0.044,"zscore":-21.4},
{"experiment":30385,"c":12,"ratio":-0.01,"stdev":0.352,"median":0.002,"zscore":-1.467},
{"experiment":30385,"c":13,"ratio":-0.03,"stdev":0.366,"median":-0.014,"zscore":-2.375},
{"experiment":30385,"c":14,"ratio":-0.039,"stdev":0.339,"median":-0.025,"zscore":-8.816},
{"experiment":30385,"c":15,"ratio":-0.02,"stdev":0.357,"median":0.0065,"zscore":-4.2},
{"experiment":30385,"c":16,"ratio":0.449,"stdev":0.439,"median":0.4215,"zscore":69.859},
{"experiment":30385,"c":17,"ratio":-0.028,"stdev":0.367,"median":-0.007,"zscore":-4.9},
{"experiment":30385,"c":18,"ratio":-0.071,"stdev":0.357,"median":-0.061,"zscore":-17.268},
{"experiment":30385,"c":19,"ratio":0.143,"stdev":0.356,"median":0.1415,"zscore":13.961},
{"experiment":30385,"c":20,"ratio":0.022,"stdev":0.349,"median":0.0405,"zscore":3.462},
{"experiment":30385,"c":21,"ratio":-0.076,"stdev":0.335,"median":-0.086,"zscore":-11.368},
{"experiment":30385,"c":22,"ratio":0.038,"stdev":0.355,"median":0.07,"zscore":3.152},
{"experiment":30385,"c":23,"ratio":0,"stdev":0,"median":0,"zscore":3.152},
{"experiment":30385,"c":24,"ratio":0,"stdev":0,"median":0,"zscore":3.152},
{"experiment":30384,"c":1,"ratio":-0.058,"stdev":0.403,"median":-0.042,"zscore":-14.154},
{"experiment":30384,"c":2,"ratio":-1.017,"stdev":0.418,"median":-0.982,"zscore":-360.857},
{"experiment":30384,"c":3,"ratio":-0.094,"stdev":0.417,"median":-0.074,"zscore":-30.964},
{"experiment":30384,"c":4,"ratio":-0.155,"stdev":0.397,"median":-0.157,"zscore":-54.593},
{"experiment":30384,"c":5,"ratio":-0.024,"stdev":0.381,"median":-0.001,"zscore":-8.125},
{"experiment":30384,"c":6,"ratio":0.013,"stdev":0.37,"median":0.0245,"zscore":7.455},
{"experiment":30384,"c":7,"ratio":-0.2,"stdev":0.434,"median":-0.171,"zscore":-56.706},
{"experiment":30384,"c":8,"ratio":-0.017,"stdev":0.367,"median":0.003,"zscore":-5.621},
{"experiment":30384,"c":9,"ratio":0.025,"stdev":0.365,"median":0.044,"zscore":6.818},
{"experiment":30384,"c":10,"ratio":-0.168,"stdev":0.422,"median":-0.121,"zscore":-44.158},
{"experiment":30384,"c":11,"ratio":-0.073,"stdev":0.382,"median":-0.056,"zscore":-22.067},
{"experiment":30384,"c":12,"ratio":0.002,"stdev":0.379,"median":0.019,"zscore":2.533},
{"experiment":30384,"c":13,"ratio":-0.054,"stdev":0.39,"median":-0.0295,"zscore":-8.375},
{"experiment":30384,"c":14,"ratio":0.019,"stdev":0.376,"median":0.025,"zscore":6.447},
{"experiment":30384,"c":15,"ratio":-0.054,"stdev":0.421,"median":-0.0265,"zscore":-11},
{"experiment":30384,"c":16,"ratio":0.055,"stdev":0.375,"median":0.0695,"zscore":8.297},
{"experiment":30384,"c":17,"ratio":0.024,"stdev":0.394,"median":0.054,"zscore":3.767},
{"experiment":30384,"c":18,"ratio":-0.049,"stdev":0.36,"median":-0.018,"zscore":-11.902},
{"experiment":30384,"c":19,"ratio":0.095,"stdev":0.37,"median":0.1135,"zscore":10.24},
{"experiment":30384,"c":20,"ratio":0.157,"stdev":0.343,"median":0.174,"zscore":29.423},
{"experiment":30384,"c":21,"ratio":-0.091,"stdev":0.407,"median":-0.067,"zscore":-14},
{"experiment":30384,"c":22,"ratio":0.071,"stdev":0.381,"median":0.104,"zscore":7.329},
{"experiment":30384,"c":23,"ratio":0,"stdev":0,"median":0,"zscore":7.329},
{"experiment":30384,"c":24,"ratio":0,"stdev":0,"median":0,"zscore":7.329}]
The data contains an experiment id, chromosome number, ratio and some satistics. The array can contain data from various experiments, which all have a different id.
my js code currently looks like this:
<script>
function unique(list) {
var result = [];
$.each(list, function(i, e) {
if ($.inArray(e, result) == -1) result.push(e);
});
return result;
}
var margin = {top: 50, right: 50, bottom: 50, left: 50};
var width = 1000 - margin.left - margin.right;
var height = 500 - margin.top - margin.bottom;
var threshold={upper:0.1,lower:-0.1};
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");
var svg = d3.select("#svg").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.json("{{settings.Base_url}}/templates/addons/data.json", function(error, data) {
if (error) throw error;
var expNames =unique(data.map(function(d) { return d.experiment; }));
x0.domain(data.map(function(d) { return d.c; }));
x1.domain(expNames).rangeRoundBands([0, x0.rangeBand()]);
y.domain([-1.5,1.5]);
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", -50)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Average Ratio/Chromosome");
var chr = svg.selectAll(".bar")
.data(data)
.enter().append("g")
.attr("class", "bar")
.attr("transform", function(d) { return "translate(" + x0(d.c) + ",0)"; });
chr.selectAll("rect")
.data(data)
.enter().append("rect")
.attr("x", function(d) {return x1(d.experiment);})
.attr("y", function(d) { return y(Math.max(0, d.ratio)); })
.attr("height", function(d) { return Math.abs(y(d.ratio)-y(0)); })
.attr("width", x1.rangeBand())
.style("fill", function(d) { return color(d.experiment); })
.style({"opacity":0.6,"stroke-width":"2"})
.text("test");
var legend = svg.selectAll(".legend")
.data(expNames.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; });
});
</script>
which results in a graph like this:
any ideas? I have a general idea where the error is, but cant seem to find a solution.
Note sure if I understand the question properly, but here's what I get:
You bind the data to the groups which you transform in x direction.
Afterwards you want to display two bars (for each experiment) in each group (c value)
If that's the case, you don't need to bind the data again for the bars, so it's just:
var chr = svg.selectAll(".bar")
.data(data)
.enter().append("g")
.attr("class", "bar")
.attr("transform", function (d) {
return "translate(" + x0(d.c) + ",0)";
});
chr.append("rect")
.attr("x", function (d) {
return x1(d.experiment);
})
...etc
See fiddle
Does that help?
Fiddle Example
I have been following these two examples (1)(2) to create small multiple grouped bar charts on the same page. Here's a JSON data example:
var data = [
{"name":"AA","sales_price":20,"retail_price":25},
{"name":"BB","sales_price":30,"retail_price":45},
{"name":"CC","sales_price":10,"retail_price":55},
{"name":"DD","sales_price":10,"retail_price":25},
{"name":"EE","sales_price":13,"retail_price":20},
{"name":"GG","sales_price":13,"retail_price":15},
];
I've managed to get the bar values to show up correctly in each chart, but the X domain and Y domain values aren't right. I couldn't figure out how to bind each data row's sales_price and retail_price to the axises instead of the entire JSON data. I guess there's a problem with this block of code:
data.forEach(function(d) {
d.compare = field_name.map(function(name) {
return {name: name, value: +d[name]};
});
});
x0.domain(data.map(function(d) { console.log(d); return d.name; }));
x1.domain(field_name).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function(d) {
return d3.max(d.compare, function(d) {
return d.value; });
})]);
How can I make the domains return each row's values for each grouped bar charts?
Full Code:
function multi_bars(el){
var margin = {top: 45, right:20, bottom: 20, left: 50},
width = 350 - margin.left - margin.right,
height = 250 - margin.top - margin.bottom;
var x0 = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var x1 = d3.scale.ordinal();
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x0)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var field_name = ['retail_price','sales_price'];
data.forEach(function(d) {
d.compare = field_name.map(function(name) {
return {name: name, value: +d[name]};
});
});
x0.domain(data.map(function(d) { console.log(d); return d.name; }));
x1.domain(field_name).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function(d) {
return d3.max(d.compare, function(d) {
return d.value; });
})]);
var svg = d3.select(el).selectAll("svg")
.data(data)
.enter().append("svg: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);
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("Price");
// Accessing nested data: https://groups.google.com/forum/#!topic/d3-js/kummm9mS4EA
// data(function(d) {return d.values;})
// this will dereference the values for nested data for each group
svg.selectAll(".bar")
.data(function(d) {return d.compare;})
.enter()
.append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x1(d.name); })
.attr("width", x1.rangeBand())
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
.attr("fill", color)
var legend = svg.selectAll(".legend")
.data(field_name.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 type(d) {
d.percent = +d.percent;
return d;
}
}
multi_bars(".container");
Your setting up of x0, x1 and y is fine.
Later when you manipulate the DOM is where your references to the data don't work.
I have done two things: First I change your first block, so you create just one svg instead of
var svg = d3.select(el).selectAll("svg")
.data(data)
.enter().append("svg:svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
Later I just followed the example of http://bl.ocks.org/mbostock/3887051 and made the changes accordingly.
The result is here:
http://jsfiddle.net/ee2todev/g61f93gx/
If you want to have separate charts for each group as in your original fiddle you just have to translate each bar with the x0 scale. Just two adjustments have to be made:
a) you have to add the group name to the d.compare so it is accessible from the corresponding data in the bar selection
data.forEach(function(d) {
d.compare = field_name.map(function(name) {
return {group: d.name, name: name, value: +d[name]};
});
});
b) In the bar selection you have to translate each group accordingly:
svg.selectAll(".bar")
.data(function(d) {return d.compare;})
.enter()
.append("rect")
.attr("class", "bar")
.attr("transform", function(d) { return "translate(" + x0(d.group) + ",0)"; })
.attr("x", function(d) { console.log("x: "+d.value); return x1(d.name); })
.attr("width", x1.rangeBand())
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
.attr("fill", color);
The complete fiddle is here: http://jsfiddle.net/ee2todev/en8sr5m4/
Two more notes:
1) I just slightly changed your code. I highly recommend using meaningful and intuitive variable/object names. This is to me the most effective way to minimize errors. This might have been the reason you got confused. So I would rename the d.compare properties, e.g. {groupName: d.name, priceType: name, value: +d[name]}. As of now, you switched the meaning of name since name suddenly refers to the price type not the grouping name as in the original data!
2) This is a nice example of selection of selections. See also http://bost.ocks.org/mike/nest/
The first selectAll selection (the svg variable) contains an Array[6] with the objects. The second selection:
svg.selectAll(".bar").data(function(d) {return d.compare;})
iterates for each element of the svg data over an Array[2] containing an object with the price type and the value. There I added the group name.
i created a stacked bar graph.
on the y axis side i have ticks with varying lengths.
what i am trying to accomplish is to align the text in the tick to the left.
this is my example:http://jsfiddle.net/2khbceut/2/
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 CHGIL21CRS-SFXCA21CRS"},
{1:5,2:5,3:10,N:20,link_name: "Link NYCNY21CRS-NYCNY22CRS"}];
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: 210},
width = 1000 - margin.left - margin.right,
height = 100 - 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)");
// });
}
as can be seen the current positioning of the tick text is to the right.
i tried the following idea:
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.selectAll("text")
.style("text-anchor", "start");
but it did not position the ticks in the desired alignment.
any ideas?
You can make the Y axis right-oriented, which will have the effect of positioning all the labels to the right of the axis, left-aligning them:
var yAxis = d3.svg.axis()
.scale(y)
.orient("right")// <- 1st step
At that point the labels would disappear, because they'll get covered up by the bars of the graph.
But then you can shift all those left-aligned labels some constant distance in the negative X direction, such that they're back on the left side of the Y axis, but still left-aligned the way you wanted. tickPadding() is a way to shift them:
var yAxis = d3.svg.axis()
.scale(y)
.orient("right")
.tickPadding(-180)
Here's your example, modified with the above: http://jsfiddle.net/2khbceut/3/
Maybe hardcoding the -180 is ok for you. If you need that amount to be dynamic, you can compute it using getBBox() on each text element of the axis and taking the maximum width to be the negative offset.
You can set the text-anchor to "start" and adjust the x position with translate, I added the code below in the chart model "boxPlotChart.js"
g.select('.nv-y.nv-axis').selectAll('.tick').selectAll('text')
.style('text-anchor','start')
.attr('transform', function(d,i,j) { return 'translate(-14,0)' });
g.select('.nv-y.nv-axis').selectAll('.nv-axisMaxMin').selectAll('text')
.style('text-anchor','start')
.attr('transform', function(d,i,j) { return 'translate(-16,0)' });
I've been searching around for a while now for a possible solution to this problem. I've created a bar chart for a company dashboard based on this graph.
http://bl.ocks.org/mbostock/3887051
This is working great, however what I would like to do now is display some of the external data that I have in text underneath the graph so for example. "Total Sales Today = ......" instead of just a monthly graph.
So I guess I'm asking is there a way to do this in d3.js using a text element or anything similar? if not pointing me to something that can would be great. Ill also add that the data is coming from a csv.
This is the code:
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("data.csv", function(error, data) {
var Names = d3.keys(data[0]).filter(function(key) { return key !== "Month"; });
data.forEach(function(d) {
d.Total = Names.map(function(name) { return {name: name, value: +d[name]}; });
});
x0.domain(data.map(function(d) { return d.Month; }));
x1.domain(Names).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function(d) { return d3.max(d.Total, 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("Sales Value £");
var text = svg.selectAll("text")
.data(data)
.enter()
.append("text");
var Month = svg.selectAll(".Month")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x0(d.Month) + ",0)"; });
Month.selectAll("rect")
.data(function(d) { return d.Total; })
.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(Names.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; });
});
If you need any more info just say
Cheers!
In your HTML file, create a div for your chart and a div below that for your label
<div id="chart"></div>
<div id="label"></div>
In your d3 code, instead of appending an svg element to the body, select the "chart" div and append an svg element to it.
var svg = d3.select("#chart").append("svg"). ...
Use that svg element to draw your chart like in your above code.
At some point in your code calculate the total sales for the day and create a variable called totalSales. You could do this by summing up the sales value when you draw the chart, but it doesn't really matter as long as totalSales is calculated.
Create another svg element on the "label" div
var svgLabel = d3.select("#label").append("svg") ...
Use this svgLabel to write a text element with totalSales as the text attribute.
svg.append("text")
...
.text(totalSales);