Constraining axis labels in d3 - d3.js

I am following a tutorial so I can learn a bit of d3js.
Here is my code:
'use strict';
//Dashboard
//setup size of line chart
var margin = {top: 20, right: 20, bottom: 30, left: 50},
width = 600 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
//parse data from file
var parseDate = d3.time.format("%b").parse;
//set scales
var x = d3.time.scale()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
//create axes
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
//construct the line using points from data
var line = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.users); });
var svg = d3.select(".linechart").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.tsv("data.tsv", function(error, data) {
if (error) throw error;
//traverse through the data
data.forEach(function(d) {
d.date = parseDate(d.date);
d.users = +d.users;
});
//establish the domain for x and y axes
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain(d3.extent(data, function(d) { return d.users; }));
//add "groups"
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("Users (unique)");
svg.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
});
The results look like this:
The data is:
date users
Jan 10
Feb 20
Mar 30
....
My question is about the axis, how can I force it to not insert labels on the x axis that are not in the data set?

Set ticks for x axis manually:
...
if (error) throw error;
var ticks = data.map(function(d) { return parseDate(d.date) };
...
x.domain(d3.extent(data, function(d) { return d.date; })).tickValues(ticks);
https://github.com/d3/d3-3.x-api-reference/blob/master/SVG-Axes.md#tickValues

Related

D3 single line chart with tsv data

I am trying to import single-column tsv data in order to create a line chart with d3.js, but I am getting an error message, and I don't know how to solve this.
Here is my code (based on already existing examples):
<script>
var margin = {top: 20, right: 20, bottom: 30, left: 50}, // as if margin would be an object
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.linear()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis() // we create a new axis
.scale(x) // we set the scale based on the var x
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var line = d3.svg.line()
.x(function(d,i) {
console.log('Plotting X value for data point: ' + d + ' using index: ' + i + ' to be at: ' + x(i) + ' using our xScale.');
return x(i);
})
.y(function(d) { return y(d.values); });
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.tsv("data.tsv", type, function(error, data) {
if (error) throw error;
x.domain(d3.extent(data, function(d) { return d.length; })); // get min and max date values
y.domain(d3.extent(data, function(d) { return d.values; }));
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis); // we call also a var!!
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("Values");
svg.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
});
function type(d) {
d.values = +d.values; // converts string to number
return d;
}
</script>
And here is the tsv file:
values
1.5
2.6
3.4
7.8
3.8
1.0
6.7
The error I get is:
"Invalid value for <path> attribute d="MNaN,416.9117647058824LNaN,344.1176470588235LNaN,291.17647058823525LNaN,0LNaN,264.70588235294116LNaN,450LNaN,72.79411764705881"
Apparently my data index is not correctly read.
Could someone maybe help?
One change instead of
x.domain(d3.extent(data, function(d) { return d.length; })); // get min and max
Do this since the x axis is the length of the array.
x.domain([0, data.length-1]); // get min and max
Working code here
Hope this helps!

Make stacked area chart overlapping area

This stacked area chart by Mike Bostock appears slightly misleading to me. For example, if you look at the numbers on the yAxis, it appears (to me) that for Monday 23, the lightest blue area has a total of approximately 95 or 96, but if you look at the data, the total for light blue (which is actually group 3) is really 46 for Monday 23. So why is that? because the areas are stacked ontop of each other, wheras the yAxis gives the impression (in my opinion), for example, that the area at the top of the stack includes all the numbers below it (hence my reading of 96 for group 3 on Monday January 23).
My question is, how can that graph (code and data below) be changed so the it works the way that I initially interpreted it, so that the raw data for any area that's ontop includes the totals below. Likely not the correct term for it, but I want to make a stacked area chart "overlapping" instead.
var format = d3.time.format("%m/%d/%y");
var margin = {top: 20, right: 30, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.time.scale()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var z = d3.scale.category20c();
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(d3.time.days);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var stack = d3.layout.stack()
.offset("zero")
.values(function(d) { return d.values; })
.x(function(d) { return d.date; })
.y(function(d) { return d.value; });
var nest = d3.nest()
.key(function(d) { return d.key; });
var area = d3.svg.area()
.interpolate("cardinal")
.x(function(d) { return x(d.date); })
.y0(function(d) { return y(d.y0); })
.y1(function(d) { return y(d.y0 + d.y); });
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) {
if (error) throw error;
data.forEach(function(d) {
d.date = format.parse(d.date);
d.value = +d.value;
});
var layers = stack(nest.entries(data));
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.y0 + d.y; })]);
svg.selectAll(".layer")
.data(layers)
.enter().append("path")
.attr("class", "layer")
.attr("d", function(d) { return area(d.values); })
.style("fill", function(d, i) { return z(i); });
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
});
data
key,value,date
Group1,37,04/23/12
Group2,12,04/23/12
Group3,46,04/23/12
Group1,32,04/24/12
Group2,19,04/24/12
Group3,42,04/24/12
Group1,45,04/25/12
Group2,16,04/25/12
Group3,44,04/25/12
Group1,24,04/26/12
Group2,52,04/26/12
Group3,64,04/26/12

transform linechart to 'squared' chart not being a barchart

I just created a linechart but what I would really like is that the line is not going from point to point, but that the line is more squared.
I don't know if this has a specific name, but these pictures should make it all a bit more clear:
This is what I have:
This is what I want:
Is there a way to do this in d3 without having to create a script which adds the 'extra' points?
This is the code I use for the line chart:
var maxDepth = graphObj[graphObj.length-1].maxDepth ;
$('#floodRiskChart').html('');
var margin = {top: 5, right: 5, bottom: 50, left: 65},
width = 410 - margin.left - margin.right,
height = 210 - margin.top - margin.bottom;
var x = d3.scale.linear()
.range([0, width]);
var y = d3.scale.linear()
.domain([0, maxDepth ])
.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.exceedance); })
.y(function(d) { return y(d.depth); });
var svg = d3.select("#chart").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 + ")");
graphObj.forEach(function(d) {
//d.date = parseDate(d.date);
d.exceedance = parseFloat(+d.exceedance);
d.depth= parseFloat(+d.depth);
});
x.domain(d3.extent(graphObj, function(d) { return parseFloat(d.exceedance); }));
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("x", "25%")
.attr("dy", "3em")
.html("chance");
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", -50)
.attr("dx", 0)
.style("text-anchor", "end")
.html("depth");
svg.append("path")
.datum(graphObj)
.attr("class", "line")
.attr("d", line);
you need to add .interpolate('step-after') on the line generator, like so:
var line = d3.svg.line()
.interpolate('step-after')
.x(function(d) { return x(d.exceedance); })
.y(function(d) { return y(d.depth); });
this will give you that result, more info can be found here: enter link description here towards the end of the page

how to create bar graph dynamically using d3 js in .net

I am new in D3 js and working on it.i wanted to implement bar graph through it in my MVC3 web application.Could you please let me know how can i create bar graph with dynamically.below is sample code but i am not able to find the dynamic creation bar graph
<script>
var margin = { top: 20, right: 20, bottom: 30, left: 50 },
width = 600 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
var parseDate = d3.time.format("%Y").parse;
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.year); })
.y(function (d) { return y(d.oil); });
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("YearlyOilProduction.csv", function (error, data)
{
data.forEach(function (d) {
d.year = parseDate(d.year);
d.oil = +d.oil;
});
x.domain(d3.extent(data, function (d) { return d.year; }));
y.domain(d3.extent(data, function (d) { return d.oil; }));
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("million Sm3");
svg.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
});
</script>

TypeError: Cannot call method 'forEach' of undefined " in d3 js on MVC Razor

Why I am getting error on below code in place of data.forEach
I added D3 js as well but code is not able to identified the "data.forEach".please let me know how to resolve this issue on MVC razor.
my data2.csv is below
date,close
1971,0.357
1972,1.927
1973,1.870
1974,2.014
1975,10.995
1976,16.227
1977,16.643
1978,20.644
1979,22.478
my scripts is below
var margin = { top: 20, right: 20, bottom: 30, left: 50 },
width = 600 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
var parseDate = d3.time.format("%Y").parse;
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.date); })
.y(function (d) { return y(d.close); });
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("data2.csv", function (error, data)
{
data.forEach(function (d)
{
d.date = parseDate(d.date);
d.close = +d.close;
});
x.domain(d3.extent(data, function (d) { return d.date; }));
y.domain(d3.extent(data, function (d) { return d.close; }));
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("million Sm3");
svg.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
});
</script>
According to the comments above, it has to do with the parsing of the date and perhaps with the loading of the csv, as Lars suspects. In any case, here is a working fiddle after changing the csv to a javascript variable.
data.forEach(function (d)
{
d.date = parseDate(d.date.toString());
d.close = +d.close;
});

Resources