d3.js line chart with negative numbers - d3.js
I have a working d3.js line chart that renders linear numbers: http://jsfiddle.net/2g9VQ/
var probArray = ["1.0", "0.999999931839", "0.999816434171", "0.994147880224", "0.961785353466", "0.882923015661", "0.763731336472", "0.627901360001", "0.497594590727", "0.385100568858", "0.256161790111", "0.168894610653", "0.111773057", "0.0747467808441", "0.051065424573", "0.0355360834346", "0.0251527408762", "0.018097770"];
var imlArray = ["2.0", "4.0", "6.0", "8.0", "10.0", "12.0", "14.0", "16.0", "18.0", "20.0", "23.0", "26.0", "29.0", "32.0", "35.0", "38.0", "41.0", "44.0", "47.0", "50.0", "55.0", "60.0", "65.0", "70.0", "75.0", "80.0", "85.0", "90.0", "95.0", "100.0", "110.0", "120.0", "130.0", "140.0", "150.0", "160.0", "170.0", "180.0", "190.0", "200.0", "220.0", "240.0", "260.0", "280.0", "300.0"];
function log(n) {
return Math.log(n) / Math.LN10;
}
var data = [];
for(i=0; i<probArray.length; i++) {
// without log values...
data.push([parseFloat(imlArray[i]), parseFloat(probArray[i])]);
// with log valuse...
//data.push([log(parseFloat(imlArray[i])), log(parseFloat(probArray[i]))]);
}
console.log(data);
var margin = {top: 20, right: 20, bottom: 50, left: 50},
width = 400 - margin.left - margin.right,
height = 320 - margin.top - margin.bottom;
var x = d3.time.scale().range([0, width]);
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");
var line = d3.svg.line()
.x(function(d) { return x(d.x); })
.y(function(d) { return y(d.y); });
var svg = d3.select("#dialog").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 dataCallback = function(d) {
d.x = +d[0];
d.y = +d[1];
};
data.forEach(dataCallback);
x.domain(d3.extent(data, function(d) { return d.x; }));
y.domain([0, d3.max(data, function(d) { return d.y; })]);
svg.append("path")
.data([data])
.attr("class", "line")
.attr("d", line);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("x", 160)
.attr("y", 30)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Intensity measure type");
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("Probabability of exceedance in "+invest_time+" years");
var legend = d3.select("#dialog").append("svg");
legend.append("text")
.attr("x", 20)
.attr("y", 7)
.attr("dy", ".35em")
.text("Location (Lon/Lat): "+lng+", "+lat);
d3.select('#chart').on("click", function() {
data.splice(0,1);
data.push([5,5]);
dataCallback(data[data.length - 1]);
x.domain(d3.extent(data, function(d) { return d.x; }));
y.domain([0, d3.max(data, function(d) { return d.y; })]);
svg.selectAll("path").data([data])
.attr("d", line);
});
But the chart fails to render when I change the points to a logarithmic scale: http://jsfiddle.net/Z3Yms/
var probArray = ["1.0", "0.999999931839", "0.999816434171", "0.994147880224", "0.961785353466", "0.882923015661", "0.763731336472", "0.627901360001", "0.497594590727", "0.385100568858", "0.256161790111", "0.168894610653", "0.111773057", "0.0747467808441", "0.051065424573", "0.0355360834346", "0.0251527408762", "0.018097770"];
var imlArray = ["2.0", "4.0", "6.0", "8.0", "10.0", "12.0", "14.0", "16.0", "18.0", "20.0", "23.0", "26.0", "29.0", "32.0", "35.0", "38.0", "41.0", "44.0", "47.0", "50.0", "55.0", "60.0", "65.0", "70.0", "75.0", "80.0", "85.0", "90.0", "95.0", "100.0", "110.0", "120.0", "130.0", "140.0", "150.0", "160.0", "170.0", "180.0", "190.0", "200.0", "220.0", "240.0", "260.0", "280.0", "300.0"];
function log(n) {
return Math.log(n) / Math.LN10;
}
var data = [];
for(i=0; i<probArray.length; i++) {
// without log values...
//data.push([parseFloat(imlArray[i]), parseFloat(probArray[i])]);
// with log valuse...
data.push([log(parseFloat(imlArray[i])), log(parseFloat(probArray[i]))]);
}
console.log(data);
var margin = {top: 20, right: 20, bottom: 50, left: 50},
width = 400 - margin.left - margin.right,
height = 320 - margin.top - margin.bottom;
var x = d3.time.scale().range([0, width]);
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");
var line = d3.svg.line()
.x(function(d) { return x(d.x); })
.y(function(d) { return y(d.y); });
var svg = d3.select("#dialog").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 dataCallback = function(d) {
d.x = +d[0];
d.y = +d[1];
};
data.forEach(dataCallback);
x.domain(d3.extent(data, function(d) { return d.x; }));
y.domain([0, d3.max(data, function(d) { return d.y; })]);
svg.append("path")
.data([data])
.attr("class", "line")
.attr("d", line);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("x", 160)
.attr("y", 30)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Intensity measure type");
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("Probabability of exceedance in "+invest_time+" years");
var legend = d3.select("#dialog").append("svg");
legend.append("text")
.attr("x", 20)
.attr("y", 7)
.attr("dy", ".35em")
.text("Location (Lon/Lat): "+lng+", "+lat);
d3.select('#chart').on("click", function() {
data.splice(0,1);
data.push([5,5]);
dataCallback(data[data.length - 1]);
x.domain(d3.extent(data, function(d) { return d.x; }));
y.domain([0, d3.max(data, function(d) { return d.y; })]);
svg.selectAll("path").data([data])
.attr("d", line);
});
I believe this is due to the numbers becoming negative along the y axis.
You were setting the domain of the y axis as
y.domain([0, d3.max(data, function(d) { return d.y; })]);
which with the log values won't work because all values are less than 0. To fix, simply use
y.domain(d3.extent(data, function(d) { return d.y; }));
instead. You also might want to use a linear scale instead of a time scale for the x axis. Complete jsfiddle here.
Related
Add labels to bar chart D3
I'm trying to add labels to my bar chart, and I can't make it work. I read a lot about it and tried many things already, but no success. var margin = {top: 20, right: 20, bottom: 70, left: 40}, width = 600 - margin.left - margin.right, height = 300 - margin.top - margin.bottom; var parseDate = d3.timeParse("%Y-%m"); var x = d3.scaleBand().rangeRound([0, width]).paddingInner(0.05); var y = d3.scaleLinear().range([height, 0]); var xAxis = d3.axisBottom(x) .tickFormat(d3.timeFormat("%Y-%m")); var yAxis = d3.axisLeft(y) .ticks(10); 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("bar-data.csv", function(error, data) { data.forEach(function(d) { d.date = parseDate(d.date); d.value = +d.value; }); x.domain(data.map(function(d) { return d.date; })); y.domain([0, d3.max(data, function(d) { return d.value; })]); 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", "-.55em") .attr("transform", "rotate(-90)" ); 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("Value ($)"); svg.selectAll("bar") .data(data) .enter().append("rect") .style("fill", "steelblue") .attr("x", function(d) { return x(d.date); }) .attr("width", x.bandwidth()) .attr("y", function(d) { return y(d.value); }) .attr("height", function(d) { return height - y(d.value); }); svg.selectAll("text") .data(data) .enter() .append("text") .text(function(d) { return d.value; }) .attr("text-anchor", "middle") .attr("fill", "white") .attr("x", function(d, i) { return i * (width / data.length); }) .attr("y", function(d) { return height - (d * 4); }); }); Here is my Plunker. Any help is appreciated. Thank you in advance.
You can add labels to the bars using the snippet below - svg.selectAll(".text") .data(data) .enter() .append("text") .attr("class","label") .attr("x", (function(d) { return x(d.date); } )) .attr("y", function(d) { return y(d.value) - 20; }) .attr("dy", ".75em") .text(function(d) { return d.value; }); Your problem was here. The correct approach to get x-y positions for the labels would be the same approach you used for the bars using the x and y variables. .attr("x", function(d, i) { return i * (width / data.length); }) .attr("y", function(d) { return height - (d * 4); }); Updated Plunkr - https://plnkr.co/edit/e6HPuph3OSbpxeEVQXfF?p=preview
Change chart colors
I have a bar chart/histogram, all working fine. I need to change the text and lines on the chart is on a black background.I Also, the bar colors need to be orange. I have had a look around the web, and seen some references to .attr("style":...); and have tried this without success. Any pointers gratefully received. <script> // set the dimensions of the canvas var margin = {top: 20, right: 20, bottom: 70, left: 40}, width = 1890 - margin.left - margin.right, height = 400 - margin.top - margin.bottom; // set the ranges var x = d3.scale.ordinal().rangeRoundBands([0, width], .05); var y = d3.scale.linear().range([height, 0]); // define the axis var xAxis = d3.svg.axis() .scale(x) .orient("bottom") var yAxis = d3.svg.axis() .scale(y) .orient("left") .ticks(10); // add the SVG element 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 + ")"); // load the data d3.json("/assets/js/risk_hist_values.json", function(error, data) { // the number of columns in this chart var numCols = data.length; data.forEach(function(d) { d.Letter = d.bin_no; d.Freq = +d.count; }); // scale the range of the data x.domain(data.map(function(d) { return d.Letter; })); y.domain([0, d3.max(data, function(d) { return d.Freq; })]); var tip = d3.tip() .attr('class', 'd3-tip') .offset([-10, 0]) .html(function(d) { return "<strong>Count:</strong> <span style='color:red'>" + d.count + "</span>"; }) // call the tips svg.call(tip); // add axis 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", "-.55em") .attr("transform", "rotate(-90)" ); svg.append("g") .attr("class", "y axis") .call(yAxis) .append("text") .attr("transform", "rotate(-90)") .attr("y", 5) .attr("dy", ".71em") .style("text-anchor", "end") .text("Count"); // Add bar chart svg.selectAll("bar") .data(data) .enter().append("rect") .attr("class", "bar") .attr("x", function(d) { return x(d.Letter); }) .attr("width", x.rangeBand()) .attr("y", function(d) { return y(d.Freq); }) .on('mouseover', tip.show) .on('mouseout', tip.hide) .on('click', drill) .attr("id", function(d, i){ return 'b_'+i+''; }) .attr("height", function(d) { return height - y(d.Freq); }); }); function drill(){ alert( 'drilling' ); } </script>
In d3.js ticks are not showing in the x axis
I am trying to show a barchart using d3.js. Y axis contains the speed and x axis contains time. I am using the following code: var margin = {top: 20, right: 20, bottom: 70, left: 40}, width = 400 - margin.left - margin.right, height = 250 - margin.top; var x = d3.scale.ordinal().rangeRoundBands([ 0, width ], .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").ticks(20); x.domain(data.map(function(d) { return d.datetime; })); y.domain([0, d3.max(data, function(d) { return d.speed; })]); var svg=d3.select("#bar").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 transition = svg.transition().duration(750), delay = function(d, i) { return i * 50; }; 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", "-.55em") .attr("transform", "rotate(-90)" ).text("Time"); 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("Speed"); svg.selectAll("rect") .data(data) .enter().append("rect").transition().delay(0) .style("fill", "red") .attr("x", function(d,i) { return 30*i+20; }) .attr("width", 25) .attr("y", function(d) { return y(d.speed); }) .attr("height", function(d) { return height - y(d.speed); }); //function(d){return " "+d.datetime;} transition.select(".y.axis").call(yAxis); But unfortunately no ticks are showing in the x axis. Y axis ticks are working fine. Can anyone help me regarding this?
In your x-axis creation, this: .attr("transform", "rotate(-90)" ).text("Time"); is just going to but the word "Time" on each tick. What you need is: // define the time format you want var format = d3.time.format("%Y-%m-%d"); // create x axis ... .text(function(d){ return format(d); }); Also in your rect placement: svg.selectAll("rect") ... .attr("x", function(d,i) { return 30*i+20; }) You are spacing them based on index. You need to match this to the axis position: svg.selectAll("rect") ... .attr("x", function(d, i) { return x(d.datetime); }) Here's an example.
Your problem may be where you set the domain for x: x.domain(data.map(function(d) { return d.datetime; })); You probably want something like: x.domain([d3.min(data, function(d) { return d.datetime; }), d3.max(data, function(d) { return d.datetime; })]);
Limitting x and y axis ticks in bar graph with d3.js
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
negative and positive values with d3js
I want to display negative and positive values with d3js... with dis code, it made only the positive values. i've worked with this : http://jsfiddle.net/chrisJamesC/tNdJj/4/ can anyone help me? her it is the code: var margin = {top: 20, right: 20, bottom: 30, left: 40}, width = 1200 - margin.left - margin.right, height = 390 - margin.top - margin.bottom; var y0 = Math.max(Math.abs(d3.min(d3.csv)), Math.abs(d3.max(d3.csv))); var y = d3.scale.linear() .domain([-y0, y0]) .range([height,0]) .nice(); var x = d3.scale.ordinal() .domain(d3.range(d3.csv.length)) .rangeRoundBands([0, width], .1); var color = d3.scale.ordinal() .range(["#FFFFFF", "#FF0000", "#FE642E", "#00FF00", "#04B404", "#a05d56", "#d0743c", "#ff8c00"]); var xAxis = d3.svg.axis() .scale(x) .orient("bottom"); var yAxis = d3.svg.axis() .scale(y) .orient("left") var chart2 = d3.select("#graph2") .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 div = d3.select("body").append("div") .attr("class", "tooltip") .style("opacity", 0); d3.csv("dataanzeigeauto3.asp", function(error, data) { color.domain(d3.keys(data[0]).filter(function(key) { return key !== "State"; })); data.forEach(function(d) { var y0 = 0 d.ages = color.domain().map(function(name) { return {name: name, y0: y0, y1: y0 += +d[name]}; }); d.total = d.ages[d.ages.length - 1].y1; }); x.domain(data.map(function(d) { return d.State; })); y.domain([0, d3.max(data, function(d) { return d.total; })]); chart2.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + height + ")") .call(xAxis); chart2.append("g") .attr("class", "y axis") .call(yAxis) .append("text") .attr("transform", "rotate(-90)") .attr("dy", ".71em") .attr("y", 6) .style("text-anchor", "end") .text("Anzahl"); var state2 = chart2.selectAll(".state") .data(data) .enter().append("g") .attr("class", "g") .attr("transform", function(d) { return "translate(" + x(d.State) + ",0)"; }); state2.selectAll("rect") .data(function(d) { return d.ages; }) .enter().append("rect") .attr("class", function(d) { return d < 0 ? "bar negative" : "bar positive"; }) .attr("y", function(d) { return y(Math.max(0, (d.y1))); }) .attr("height", function(d) { return Math.abs(y(d.y0) - y(d.y1)); }) .attr("width", x.rangeBand()) .style("fill", function(d) { return color(d.name); }) .call(d3.helper.tooltip() .attr({class: 'tooltip2'}) .text(function(d, i){ return d.name + ': '+ (-((d.y0) - (d.y1))) ; }) ); var legend2 = chart2.selectAll(".legend") .data(color.domain().slice().reverse()) .enter().append("g") .attr("class", "legend") .attr("transform", function(d, i) { return "translate(0," + i * 100 + ")"; }); legend2.append("rect") .attr("x", width - 30) .attr("width", 18) .attr("height", 18) .style("fill", color); legend2.append("text") .attr("x", width - 35) .attr("y", 14) .attr("dy", ".35em") .style("text-anchor", "end") .text(function(d) { return d; }); });