parseDate = d3.time.format("%Y") not working - d3.js

My chart is working completely as expected, except that my parseDate function doesn't want to give me correct dates on the x-axis. I'm sure this is something simple.
Currently I'm adding d3.v2.min.js, without any additional helper libraries - do I need something else to get d3.time.format() working?
Without parsing the date my data returns an x-axis with:
.960 .965 .970 .975
rather than
1960 1965 1970 1975
The JSON is structured like this:
[{"year":1959,"average":315.97},
{"year":1960,"average":316.91},
{"year":1961,"average":317.64},
...etc
{"year":2011,"average":391.57}]
The code with the // commented sections being the issue:
<script>
var margin = {top: 20, right: 20, bottom: 30, left: 50},
width = 920 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// parseDate below not working
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");
// data taking format
d3.json("{{ asset('js/data_co2.json') }}", function(data) {
// parseDate() not working below
Gives error: Uncaught TypeError: Object 1959 has no method 'substring'
data.forEach(function(d) {
d.year = parseDate(d.year);
d.average = +d.average;
});
x.domain(d3.extent(data, function(d) { return d.year; }));
y.domain([260, 420]);
var area = d3.svg.area()
.x(function(d) { return x(d.year); })
.y0(height)
.y1(function(d) { return y(d.average); });
var svg = d3.select("#co2").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.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area);
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("CO2 Levels (ppm)");
});
</script>

parseDate, the function "prepared" by d3, expects to be passed a string for parsing. Your years are (appropriately) numbers, so you would need to convert them to strings:
parseDate(String(d.year));
or
parseDate(d.year.toString());
However, keep in mind that the same result – a Date object – could be achieved natively, like this:
var date = new Date(d.year, 0);// The required 0, is for January

Related

Parsing a dataset of time in the format "Y-M-D H:M:S.MS" gives me 0NaN-NaN-NaNTNaN:NaN:NaN.NaNZ

I am able to see glucose readings but time shows up as: 0NaN-NaN-NaNTNaN:NaN:NaN.NaNZ
I am trying to parse a dataset of time of the format "Y-M-D H:M:S.MS". I need it to be formatted properly so that I can show it on the x axis. I have attached sample dataset to this code.
My code looks like this:
<script>
function overview(){
// Set the dimensions of the canvas / graph
var margin = {top: 10, right: 20, bottom: 30, left: 30},
width = 600 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// // Parse the date / time
var parseDate = d3.utcFormat("%Y-%m-%dT%H:%M:%S.%LZ");
var x = d3.scaleTime()
.range([0, width]);
var y = d3.scaleLinear()
.range([height, 0]);
var xAxis = d3.axisBottom()
.tickFormat(d3.timeFormat("%H"));
var yAxis = d3.axisLeft();
// Define the line
var valueline = d3.line()
.x(function(d) { return x(d.time); })
.y(function(d) { return y(d.glucoseReading); });
// Adds the svg canvas
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 + ")");
// Get the data
d3.csv("glucose.csv", function(error, data) {
data.forEach(function(d) {
d.time = parseDate(d.time);
d.glucoseReading = +d.glucoseReading;
console.log(d.time);
console.log(d.glucoseReading);
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.time; }));
y.domain([0, d3.max(data, function(d) { return d.glucoseReading; })]);
// Add the valueline path.
svg.append("path")
.attr("class", "line")
.attr("d", valueline(data));
// Add the scatterplot
svg.selectAll("dot")
.data(data)
.enter().append("circle")
.attr("r", 3.5)
.attr("cx", function(d) { return x(d.time); })
.attr("cy", function(d) { return y(d.glucoseReading); });
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
});
}
overview();
</script>
My Dataset looks like:
You want to convert (parse) strings to dates, not the other way around. Therefore, instead of d3.utcFormat(), you have to use d3.utcParse(). On top of that, your specifier is incorrect: there is no timezone in your strings.
So, this should be your parseDate function and specifier:
var parseDate = d3.utcParse("%Y-%m-%d %H:%M:%S.%L")
Here is it working (check your browse console, not the snippet's one):
var parseDate = d3.utcParse("%Y-%m-%d %H:%M:%S.%L")
var string = "2017-08-23 00:03:52.591";
console.log(parseDate(string))
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

Constraining axis labels in d3

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

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!

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

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